W-Project Smart Contracts

It is important to always check that the contract code is not malicious and that the address is correct (see the address list page). In this section we provide a detailed reference of the functions of our contracts so that users and developers can verify them.

User functions

Users can directly interact with our smart contracts without the need for an app using a block explorer such as Etherscan (see the get started page for detailed instructions) using the deposit() and withdraw() functions.

Code of the deposit() function

deposit()

Our smart contracts feature a "deposit()" function that enables users to wrap their stablecoin tokens.

This function accepts one parameter, amount, which represents the number of stablecoin tokens to be wrapped. The amount should be expressed using 18 decimal places.

To avoid any rounding errors, the function starts by calculating unWrappedAmount, which adjusts the decimal places from 18 to the appropriate number for the specific token. Then it multiplies the unWrappedAmount again, reversing the previous operation and getting back the original amount without rounding errors.

After the amount is correctly adjusted, the function transfers this unWrappedAmount from the user's address to the contract's address.

Finally, the "fulfillDeposit()" function is called to finalize the deposit operation. This function checks that the contract has received a sufficient number of tokens and mints the equivalent number of wrapped tokens to the user.

Code of the withdraw() function

withdraw()

The "withdraw()" function in our smart contracts allows users to unwrap their stablecoin tokens.

This function accepts one parameter, amount, which represents the number of stablecoin tokens to be unwrapped. The amount should be expressed using 18 decimal places.

The function simply calls the "withdrawTo()" function to execute the withdrawal operation, passing the caller's address and the amount as arguments.

The "withdrawTo()" function first calculates the unWrappedAmount, similar to the deposit operation, to adjust the decimal places from 18 to the specific token's decimal places.

After adjusting for potential rounding errors, the function burns the wrapped tokens from the user's account. Then, the function transfers the unWrappedAmount of stablecoin tokens to the user.

Finally, the function checks that  number of tokens in the contract is at least equal to the total supply of wrapped tokens after the withdrawal.

Developer functions

Our smart contracts include low-level methods that allow direct integration with other contracts, avoiding users having to approve our contract, with the consequent gas savings. In this section we provide a description and examples of how to carry out the integration of W-Project with other protocols.

Code of the fulfillDeposit() function

fulfillDeposit()

The fulfillDeposit() function is a lower-level function designed to make it easy to integrate our wrapped tokens into the smart contracts of other projects.

This function has the main advantage that it avoids the need for users, or the integration contract, to approve the contract for our wrapped token. Eliminating this step saves gas and abstracts users from the underlying complexity. Instead, the function verifies that the transfer step has already been executed by checking if the token balance in the contract is sufficient. If this requirement is met, it mints the wrapped tokens for the sender of the transaction.

The fulfillDeposit() function accepts one parameter: the amount of stablecoin tokens to be wrapped. The amount should be expressed using 18 decimal places.

First, the function calculates the wrappedBalance, which is the total balance of tokens in the contract adjusted to 18 decimal places.

Next, the function checks if the wrappedBalance is greater than or equal to the sum of the total supply of wrapped tokens and the amount to be deposited. This check is crucial as it ensures that the contract has received sufficient stablecoin tokens for wrapping.

Finally, if the requirement is met, the function mints the corresponding number of wrapped tokens to the transaction sender.

Please note: Direct manual use of this function by users is not advised due to potential "front-running" attacks, where a malicious actor could mint wrapped tokens for themselves. This function should only be safely called from a smart contract as the token transfer and the function call are executed atomically in a single transaction, thus thwarting the aforementioned attack.

Code of the withdrawTo() function

withdrawTo()

The withdrawTo() function is a lower-level function, facilitating effortless integration of unwrapping tokens into other project's smart contracts. Unlike the fulfillDeposit() function, withdrawTo() can be safely manually invoked by users.

The distinction between the withdraw() function, documented earlier, and withdrawTo() is that the latter allows for unwrapped stablecoin tokens to be sent to an address different from the transaction sender. This feature is particularly beneficial when the sender is another smart contract looking to return funds to a user.

The withdrawTo() function takes in two parameters: the address where unwrapped tokens are to be sent, and the quantity of wrapped tokens to be unwrapped. The amount should be expressed using 18 decimal places.

First, it calculates the unWrappedAmount, which is the number of stablecoin tokens to be unwrapped, adjusted to the token's decimals. The function then adjusts the input amount to remove any rounding errors that could occur due to decimal precision.

Following this, it burns the specified amount of wrapped tokens from the sender's balance and transfers the unwrapped stablecoin tokens to the address specified by the parameter "to".

Finally, it verifies that the resulting balance of wrapped tokens in the contract, adjusted to 18 decimals, is equal to or greater than the total supply of wrapped tokens. This check of the contract invariant in each transaction guarantees the safety of the contract funds.

Code of integration example

Integration example

In this example, we demonstrate how to integrate W-Project wrapped tokens into a test smart contract. The code features two functions, wrapUserDeposit() and unwrapUserWithdrawal(), which represent the two primary interactions a user might have with the contract - depositing (wrapping) and withdrawing (unwrapping).

It's important to note that the second step in both functions, where the amount parameter's decimals are adjusted to 18, depends on how the User Interface (UI) sends the amount to this contract. If the UI always uses 18 decimals, this adjustment may not be necessary. On the other hand, if the UI uses the number of decimals defined by the token to be wrapped, then this adjustment becomes relevant.

In the wrapUserDeposit() function, the contract first transfers the user's tokens from the user's address to the wrapped token contract, bypassing the need to first send tokens to this contract. Next, it calculates wAmount, which is the amount to be wrapped, adjusted to 18 decimal places. Lastly, it calls fulfillDeposit() on the wrapped token contract, minting the equivalent amount of wrapped tokens for the contract.

The unwrapUserWithdrawal() function is similar. It begins by calculating wAmount, adjusting to 18 decimals if necessary. It then calls withdrawTo() on the wrapped token contract, which unwraps the tokens and transfers the underlying tokens to the user.

Fee on transfer support

Taking advantage of this coding pattern, it is also possible to create hooks for W-Project smart contracts that support tokens that have a fee-on-transfer implemented, simply by increasing the amount of the token sent compared to the amount you want to wrap, or reduce the amount that you want to unwrap in the amount of the fee.