Sections below describes the following :
- The purpose of contract
- Structure and specifications
- Local build environment
- Running unit tests for the contracts
- Overview
- General Flow
- Roles and Privileges
- Fees
- Sell On Fees DEX V2
- Immutable Parameters
- Mutable Fields
- Events
- Transitions
- Transitions DEX V2
- Modified ZRC2 for Xcad Content Creator Tokens
- Modified ZRC2 for Xcad Content Creator Tokens V2 - Mintable Burnable
- Modified ZRC2 for Xcad Content Creator Tokens V3 - Mintable Burnable Sell On Fees
- Errors
- Unit tests
- DEX Upgrade
- Local Build environment
- Zilswap Issue
Contract Name | File and Location | Description |
---|---|---|
FungibleToken | FungibleToken.scilla |
A ZRC-2 for fungible token contract for XCAD tokens |
FungibleToken With Dex Whitelisting | XcadCC_FungibleToken.scilla |
A ZRC-2 for fungible token contract for XCAD CC tokens. This includes features for DEX whitelisting and BatchTransfer of tokens to multiple addresses. Documentation can be found here. |
FungibleToken With Dex Whitelisting, Burnable, Mintable | XcadCC_FungibleToken_V2.scilla |
A ZRC-2 for fungible token contract for XCAD CC tokens. This includes features for DEX whitelisting and BatchTransfer of tokens to multiple addressed, Minatble, Burnable and Minters List. Documentation can be found here. |
FungibleToken With Dex Whitelisting, Burnable, Mintable, Sell On Fee | XcadCC_FungibleToken_V3.scilla |
A ZRC-2 for fungible token contract for XCAD CC tokens. This includes features for DEX whitelisting and BatchTransfer of tokens to multiple addressed, Minatble, Burnable, Minters List and Sell On Fee. Documentation can be found here. |
DEX_Xcad_Proxy | DEX_Xcad_Proxy.scilla |
It is a proxy contract that sits on top of the DEX_Xcad contract. Any call to the DEX_Xcad contract must come from DEX_Xcad_Proxy. This contract facilitates upgradeability of the DEX_Xcad contract in case a bug is found. |
DEX_Xcad | DEX_Xcad.scilla |
It is the main contract that keeps track of DEX on Xcademy. |
DEX_Xcad_Proxy V2 Sell On Fees Feature | DEX_Xcad_Proxy_V2.scilla |
It is a proxy contract that sits on top of the DEX_Xcad contract. Any call to the DEX_Xcad contract must come from DEX_Xcad_Proxy. This contract facilitates upgradeability of the DEX_Xcad contract in case a bug is found. |
DEX_Xcad V2 Sell On Fees Feature | DEX_Xcad_V2.scilla |
It is the main contract that keeps track of DEX on Xcademy. |
Note : It is recommended to use multi-sig wallets for ownership of DEX, Proxy and Fungible Tokens.
Proxy, once deployed, should not be changed. As such. the proxy should not perform any additional logic beyond transfering of calls to the implementation. Any roles or permissions check should be strictly done on implementation.
By consolidating all logical checks in the implementation, the benefits are
- Possbility of making logical modification in future implementation
- Avoid conflicting check between proxy and implementation
Units in the DEX are based on the smallest units of each tokens. This avoids the handling of any floating points.
For creation of new pool, a minimal liqudity of at least 100(in smallest unit value for the token) is enforced. This can be modified via SetTokenMinLiquidity
transition. This is to offset any rounding issues when liquidity of the pool is low.
Users and dApps may use the DEX_Xcad_Proxy contract to swap between any 2 tokens without any middlemen. Because the swap is done against the liquidity pools, there is no need to wait for a counterparty, and transactions can be done completely on-chain.
The table below list the different roles defined in the contract.
Name | Description & Privileges |
---|---|
admin |
The admin of proxy contract for admin functionalities. |
owner |
The owner who own the DEX contract. |
treasury_fees_address |
The address which receives the treasury amount collected in trades. |
There is a 0.3% fee(configurable) for swapping tokens. This fee is split by liquidity providers proportional to their contribution to liquidity reserves. Swapping fees are immediately deposited into liquidity reserves.
The dex contract allows for charging an additional fee per swap transaction, which goes to the dex treasury. Such fees (configuration) will be deducted from the input token amount(applies to both case when it is limit or exact amount). The contract owner of the dex can initiate withdrawal of the treasury fees to a designated address treasury_fees_address
at any point in time.
basis point denon in dex = 10000
pool fee = 2000 (20%)
treasury fee = 1000 (10%)
Based on above numbers :
pool_fee
= 8000
treasury_fee
= 1000
after_total_fee
= fee_denom
- (pool_fee
+ treasury_fee
) = 7000
Below example illustrates the calculation(token0 and token1 are based on two decimal points)
Swap 100 token0 into token 1. (pool token0 reserve = 1000.00, token1 reserve = 1000.00)
With fee as 7000
calculate output amount
calculated_amount
= 65.42
treasury_fee_amount
= 10 %
of exact_amount
= 10 % of 100.00 = 10.00
amount to add in reserve amount(token0) = 100.00 - 10.00 = 90.00
updated token0 pool reserve = 1000.00 + 90.00 = 1090.00
updated token1 pool reserve = 1000.00 - calculated_amount = 1000.00 - 65.42 = 934.58
treasury_fee_amount(token0) = 10.00
Swap some token0 into 100 token 1. (pool token0 reserve = 1000.00, token1 reserve = 1000.00)
With fee as 7000
calculate output amount
calculated_amount
= 158.73
treasury_fee_amount
= 10 %
of calculated_amount
= 10 % of 158.73 = 15.87
amount to add in reserve amount(token0) = 158.73 - 15.87 = 142.86
updated token0 pool reserve = 1000.00 + 142.86 = 1142.86
updated token1 pool reserve = 1000.00 - exact_amount = 1000.00 - 100.00 = 900.00
treasury_fee_amount(token0) = 15.87
The DEX contract V2 supports tokens sell on fees feature. This feature is implemented in a similar way to NFT roalty fees. This fee as the name implies only applies to the selling of a token, it does not aply to the buying of the tokens. This fee is an additional fee to the exisiting Swap fees in the DEX V1. The fees are accumulated in the DEX V2 contract. The contract owner of the DEX can initiate withdrawal of this Sell On Fee to a designated address sell_fee_recipient
at any point in time. The withrawal can also be initiated by the designated sell_fee_recipient
at any point in time.
The Sell On Fee rate is defined in the Content Creator Token smart contract.
Each CC Token that wants to charge sell on fee will have these two fields
sell_fee_bps
sell on fee rate in bps e.g. 500 for 5%. Valid range is 0 - 10000
sell_fee_recipient
Address of account to receive sell on fee
The CC Token Contract owner can update the settings by calling
SetSellFeeBPS
SetSellFeeRecipient
DEX V2 smart contract dectects when it is a sell, i.e. looking at the Input Token
it reads the sell_fee_bps
from the CCT contract
if sell_fee_bps
is not found then sell on fee is taken to be 0
The DEX contract then calculate the fee
sell fee amount = swap amount * sell_fee_bps / 10000
The sell on fees are accumulated in the DEX V2 smart contract, not sent to receiver on every sell.
sell_fee_balances
hold sell on fees
The Sell on Fees can be withdrawn by DEX V2 contract_owner or the sell_on_fee_recipient
Contract_Owner can call
WithdrawSellFee(initiator : ByStr20, token_address : ByStr20)
to send sell on fee for the given Token to the sell_fee_recipient
defined in the Token contract
The DEX V 2 contract:
reads fee amount accumulated in sell_on_fee_balances
reads sell_fee_recipient
from CC Token contract
transfers the fee to the recipient
BatchWithdrawSellFees(initiator: ByStr20, token_list: List (ByStr20))
to send sell on fees for a list of tokens
Sell_Fee_Recipient can also initiate sell fee withdraw, can call
WithdrawSellFeeByRecipient(initiator : ByStr20, token_address : ByStr20)
to send sell on fee for the given token to the recipient
So in the DEX V2 smart contract we have this fee structure
-
Swap Fee this is the existing fee in DEX V1, it consists of LPProvider fee and Treasury Fee, this fee applies to all swaps ( both buy and sell)
-
Sell On Fee, this applies only to Sell of tokens that has this sell on fee feature.
Example Fees: If the fee settings are: Liquidity Provider Fee = 16 Treasury Fee = 14 Sell On Fee = 500
For or a sell of of a Content Creator Token we may have these fees
0.16% of swap amount goes to Liquidity Pool
0.14% of swap amount goes to Treasury
5.00% of swap amount goes to Sell_Fee_Balance
For a buy of Content Creator token
0.16% of swap amount goes to Liquidity Pool
0.14% of swap amount goes to Treasury
For buy or sell of other tokens - non sell on fee tokens
0.16% of swap amount goes to Liquidity Pool
0.14% of swap amount goes to Treasury
token0 <=> token1
-
DirectSwapExactToken0ToToken1 token0,token1 (input is exact amount and output is limit amount)
-
DirectSwapToken0ToExactToken1 token0,token1 (input is limit amount and output is exact amount)
-
DirectSwapExactToken0ToToken1 token1,token0 (input is exact amount and output is limit amount)
-
DirectSwapToken0ToExactToken1 token1,token0 (input is limit amount and output is exact amount)
The table below lists the parameters that are defined at the contract deployment time and hence cannot be changed later on.
Name | Type | Description |
---|---|---|
initial_owner |
ByStr20 |
The initial owner of the contract. |
initial_implementation |
ByStr20 |
The initial implementation of the DEX_Xcad contract. |
Name | Type | Description |
---|---|---|
initial_owner |
ByStr20 |
The initial admin of the contract. |
initial_pool_fee |
Uint256 |
The initial fee is set |
initial_proxy_address |
ByStr20 |
The initial address of the DEX_Xcad_Proxy contract |
initial_treasury_fees_address |
ByStr20 |
The initial designated address for withdrawal of treasury fees collected. |
initial_treasury_fee |
Uint256 |
This includes percentage to be deducted from exact amount provided in basis point(10000 ) eg; if treasury fee to deduct is 0.01 % then initial_treasury_fee to be passed should be 1 . If no treasury fee is to be applied then keep initial_treasury_fee as 0 . |
The table below presents the mutable fields of the contract and their initial values.
Name | Type | Initial Value | Description |
---|---|---|---|
implementation |
ByStr20 |
initial_implementation |
Curent implementation of main contract. |
old_implementation |
ByStr20 |
initial_implementation |
Older implementation of main contract required for upgrading. |
owner |
ByStr20 |
initial_owner |
Admin of DEX_Xcad_Proxy for changing implementation contracts. |
pending_owner |
ByStr20 |
zil_address |
Staged Admin by Current admin while transferring ownership |
Name | Type | Initial Value | Description |
---|---|---|---|
pools |
Map ByStr20 Pool |
Emp ByStr20 Pool |
Map of Pools. |
xpools |
Map String XPool |
Emp String XPool |
Map of XPools. XPool stores non-zil related pool reserves. |
balances |
Map ByStr20 (Map ByStr20 Uint128) |
Emp ByStr20 (Map ByStr20 Uint128) |
Map of balances. |
xbalances |
Map String (Map ByStr20 (Map ByStr20 Uint128)) |
Emp String (Map ByStr20 (Map ByStr20 Uint128) |
Map of xbalances. Related to token to token swap which does not involve zils. |
total_contributions |
Map String (Map ByStr20 Uint128) |
Emp String (Map ByStr20 Uint128) |
Map of total contributions for non zil related pools. |
xtotal_contributions |
Map ByStr20 Uint128 |
Emp ByStr20 Uint128 |
Map of total contributions. |
output_after_total_fee |
Uint256 |
fee_denom minus (initial_pool_fee plus treasury_fee) |
Output after all fee. |
treasury_fee |
Uint256 |
initial_treasury_fee |
Treasury fee to be applied on exact amount input. |
owner |
ByStr20 |
initial_owner |
Current owner. |
pending_owner |
ByStr20 |
zil_address |
Pending owner. |
token_pairs |
Map ByStr20 (Map ByStr20 Uint128) |
Emp ByStr20 (Map ByStr20 Uint128) |
Map of token pairs. |
paused |
Bool |
True |
Status of whether the contract is paused. |
treasury_balances |
Map ByStr20 Uint128 |
Emp ByStr20 Uint128 | Map of token address and treasury amount |
Frontend dApps may listen to the following smart contract events to watch for changes in state due to user interaction.
The FeeSet
is emitted when the contract owner sets a new fee. It is emitted even if the new
fee is the same as the old fee.
Parameter | Type | Description |
---|---|---|
fee | Uint256 | The fee in basis points (1 = 0.01%) |
The OwnershipTransferred
is emitted when the existing contract owner transfers ownership to a
new address.
Parameter | Type | Description |
---|---|---|
new_owner | ByStr20 | The address of the new owner |
The PoolCreated
event is emitted when liquidity is first added for a ZRC-2 token.
Pools are indexed by the ZRC-2 token's smart contract address, emitted in the parameter, pool
.
Parameter | Type | Description |
---|---|---|
pool | ByStr20 | The ZRC-2 token address for the pool that was created |
The XPoolCreated
event is emitted when liquidity is first added for a pair of ZRC-2 tokens.
XPools are indexed by the concatenation of ZRC-2 token's smart contract addresses delimited by comma, emitted in the parameter, xpool
.
Parameter | Type | Description |
---|---|---|
address | ByStr20 | Address of pool creator |
token0_address | ByStr20 | The ZRC-2 token0 address for the xpool that was created |
token1_address | ByStr20 | The ZRC-2 token1 address for the xpool that was created |
token0_amount | Uint128 | The token0 amount of tokens contributed to xpool |
token1_amount | Uint128 | The token1 amount of tokens contributed to xpool |
The AddNewTokenLiquidity
event is emitted when liquidity has been added in xpool.
Parameter | Type | Description |
---|---|---|
address | ByStr20 | Address of pool creator |
token0_address | ByStr20 | The ZRC-2 token0 address for the liquidity pool contribution |
token1_address | ByStr20 | The ZRC-2 token1 address for the liquidity pool contribution |
token0_contribution | Uint128 | The token0 amount of tokens contributed to xpool |
token1_contribution | Uint128 | The token1 amount of tokens contributed to xpool |
The Mint
event is emitted when liquidity is added to a pool and "liquidity tokens" are "minted".
The amount of liquidity tokens minted is calculated by taking the ratio of pool tokens added as compared to that already residing in the pool.
The share of liquidity contribution for a pool can then be found by dividing amount
with the smart contract variable total_contributions[token_address]
.
Parameter | Type | Description |
---|---|---|
pool | ByStr20 | The token address of the pool that has had liquidity added to |
address | ByStr20 | The address that contributed liquidity |
amount | Uint128 | The amount of liquidity contributed |
The Burnt
event is emitted when liquidity is removed from a pool and "liquidity tokens" are "burnt".
The amount of liquidity tokens burnt is calculated by taking the ratio of pool tokens removed as compared to the total pool tokens residing in the pool.
The share of liquidity contribution removed for the address can be found by dividing amount
with the smart contract variable total_contributions[token_address]
.
Parameter | Type | Description |
---|---|---|
pool | ByStr20 | The token address of the pool that has had liquidity removed |
address | ByStr20 | The address that removed liquidity |
amount | Uint128 | The amount of liquidity removed |
The BurntXPool
event is emitted when liquidity is removed from a xpool and "liquidity tokens" are "burnt".
The amount of liquidity tokens burnt is calculated by taking the ratio of xpool tokens removed as compared to the total xpool tokens residing in the xpool.
pool: xpool_t0_t1_key; address: initiator; token0_amount: token0_contribution_amount
Parameter | Type | Description |
---|---|---|
pool | String | The string concatenation of token0 address, token1 address delimited by comma of the xpool that has had liquidity removed |
address | ByStr20 | The address that removed liquidity |
token0_amount | Uint128 | The amount of liquidity removed |
The TransferZils
event is emitted when ZILs from old dex is moved to new dex".
Parameter | Type | Description |
---|---|---|
new_implementation | ByStr20 | The address of dex contract which received ZILs. |
initiator | ByStr20 | The address that invoked the TransferZils transition in DEX |
amount | Uint128 | The amount of ZILs moved |
The TransferTokens
event is emitted when tokens from Pool in old dex is moved to new dex".
Parameter | Type | Description |
---|---|---|
new_implementation | ByStr20 | The address of dex contract which received ZILs. |
initiator | ByStr20 | The address that invoked the TransferTokens transition in DEX |
amount | Uint128 | The amount of tokens moved |
token_address | ByStr20 | The address of token moved |
The TransferXTokens
event is emitted when tokens from XPool in old dex is moved to new dex".
Parameter | Type | Description |
---|---|---|
new_implementation | ByStr20 | The address of dex contract which received ZILs. |
initiator | ByStr20 | The address that invoked the TransferXTokens transition in DEX |
t0_amount | Uint128 | The token0 amount moved |
t1_amount | Uint128 | The token0 amount moved |
token0_address | ByStr20 | The address of token0 moved |
token1_address | ByStr20 | The address of token1 moved |
The Swapped
event is emitted when a swap is made in a pool.
Since pools are already balanced against ZIL
, all events have a zil_in
and zil_out
amount to signify the amount of ZIL
sent to or received from the pool during the swap respectively.
All events also have the token_in
and token_out
to signify the corresponding token amounts sent to or received from the pool.
The address of the token sent or received in token_in
and token_out
is given by token_address
.
Note that if zil_in
is non-zero, then zil_out
will always be zero, and vice versa. The same goes for token_in
and token_out
.
Parameter | Type | Description |
---|---|---|
pool | ByStr20 | The token address of the pool where the swap took place |
address | ByStr20 | The address that initiated the swap |
zil_in | Uint128 | The amount of zil transferred into the pool for the swap in QA |
zil_out | Uint128 | The amount of zil removed from the pool for the swap in QA |
token_in | Uint128 | The amount of tokens transferred into the pool for the swap |
token_out | Uint128 | The amount of tokens removed rrom the pool for the swap |
The XSwapped
event is emitted when a swap is made in a xpool(token to token pool).
All events also have the token_in
and token_out
to signify the corresponding token amounts sent to or received from the xpool.
The address of the token sent or received in token_in
and token_out
is given by token_address
.
Parameter | Type | Description |
---|---|---|
pool | String | The concatenated string of token0 address and token1 address delimited by a comma for pool where the swap took place |
address | The address that initiated the swap. | |
token0_address | ByStr20 | The address of token0 |
token0_address | ByStr20 | The address of token1 |
input | Coins | The ADT that includes token address, input exact amount for swap |
output | Coins | The ADT that includes token address, calculated output amount for swap |
The TokenPairAdded
event is emitted when a new token pair is added in dex.
Parameter | Type | Description |
---|---|---|
token0_address | ByStr20 | The address of token0 |
token0_address | ByStr20 | The address of token1 |
The TokenPairRemoved
event is emitted when a token pair is removed dex.
Parameter | Type | Description |
---|---|---|
token0_address | ByStr20 | The address of token0 |
token0_address | ByStr20 | The address of token1 |
Note that each of the transitions in the DEX_Xcad
contract takes initiator
as a parameter which as explained above is the caller that calls the DEX_Xcad_Proxy
contract which in turn calls the DEX_Xcad
contract.
Note: No transition in the
DEX_Xcad
contract can be invoked directly. Any call to theDEX_Xcad
contract must come from theDEX_Xcad_Proxy
contract.
Transition Name | Params | Callable when paused? | Callable when not paused? |
---|---|---|---|
AddTokenLiquidity | token0_address : ByStr20, token1_address : ByStr20, token0_amount : Uint128, min_token0_contribution_amount : Uint128, max_token1_contribution_amount : Uint128, deadline_block : BNum |
❌ | ✔️ |
RemoveTokenLiquidity | token0_address : ByStr20, token1_address : ByStr20, token0_contribution_amount : Uint128, min_token0_amount : Uint128, min_token1_amount : Uint128, deadline_block : BNum |
❌ | ✔️ |
DirectSwapTokens0ToExactTokens1 | token0_address : ByStr20, token1_address : ByStr20, token0_amount : Uint128, token1_amount : Uint128, deadline_block : BNum, recipient_address : ByStr20 |
❌ | ✔️ |
DirectSwapExactTokens0ToTokens1 | token0_address : ByStr20, token1_address : ByStr20, token0_amount : Uint128, token1_amount : Uint128, deadline_block : BNum, recipient_address : ByStr20 |
❌ | ✔️ |
AddLiquidity | token_address : ByStr20, min_contribution_amount : Uint128, max_token_amount : Uint128, deadline_block : BNum |
❌ | ✔️ |
RemoveLiquidity | token_address : ByStr20, contribution_amount : Uint128, min_zil_amount : Uint128, min_token_amount : Uint128, deadline_block : BNum |
❌ | ✔️ |
SwapExactZILForTokens | token_address : ByStr20, min_token_amount : Uint128, deadline_block : BNum, recipient_address : ByStr20 |
❌ | ✔️ |
SwapExactTokensForZIL | token_address : ByStr20, token_amount : Uint128, min_zil_amount : Uint128, deadline_block : BNum, recipient_address : ByStr20 |
❌ | ✔️ |
SwapZILForExactTokens | token_address : ByStr20, token_amount : Uint128, deadline_block : BNum, recipient_address : ByStr20 |
❌ | ✔️ |
SwapTokensForExactZIL | token_address : ByStr20, max_token_amount : Uint128, zil_amount : Uint128, deadline_block : BNum, recipient_address : ByStr20 |
❌ | ✔️ |
SwapExactTokensForTokens | token0_address : ByStr20, token1_address : ByStr20, token0_amount : Uint128, min_token1_amount : Uint128, deadline_block : BNum, recipient_address : ByStr20 |
❌ | ✔️ |
SwapTokensForExactTokens | token0_address : ByStr20, token1_address : ByStr20, max_token0_amount : Uint128, token1_amount : Uint128, deadline_block : BNum, recipient_address : ByStr20 |
❌ | ✔️ |
UpgradePool | token_address: ByStr20 | ✔️ | ❌ |
UpgradeXPool | token0_address: ByStr20, token1_address: ByStr20 |
✔️ | ❌ |
PopulatePool | initiator:ByStr20, token_address: ByStr20, zil_reserve: Uint128, token_reserve: Uint128 |
✔️ | ❌ |
PopulateXPool | initiator:ByStr20, token0_address: ByStr20, token1_address: ByStr20, token0_reserve: Uint128, token1_reserve: Uint128 |
✔️ | ❌ |
UpgradeBalances | ✔️ | ❌ | |
UpgradeXBalances | ✔️ | ❌ | |
UpgradeTotalContrib | ✔️ | ❌ | |
UpgradeXTotalContrib | ✔️ | ❌ | |
TransferZils | ✔️ | ❌ | |
TransferTokens | token_address: ByStr20 | ✔️ | ❌ |
TransferTreasuryTokens | token_address: ByStr20 | ✔️ | ❌ |
TransferXTokens | token0_address: ByStr20, token1_address: ByStr20 |
✔️ | ❌ |
Pause | ✔️ | ✔️ | |
UnPause | ✔️ | ✔️ | |
TransferOwnership | new_owner : ByStr20 | ✔️ | ✔️ |
AcceptPendingOwnership | ✔️ | ✔️ | |
AddTokenPair | token0_address : ByStr20, token1_address : ByStr20 |
✔️ | ✔️ |
RemoveTokenPair | token0_address : ByStr20, token1_address : ByStr20 |
✔️ | ✔️ |
SetFee | new_pool_fee : Uint256, new_treasury_fee : Uint256 | ✔️ | ✔️ |
SetTreasuryFeesAddress | new_treasury_fees_address : ByStr20 | ✔️ | ✔️ |
WithdrawTreasuryTokens | token_address : ByStr20 | ✔️ | ✔️ |
BatchWithdrawTreasuryTokens | token_list: List (ByStr20) | ✔️ | ✔️ |
WithdrawTreasuryZils | ✔️ | ✔️ | |
UpgradeTreasuryBalances | ✔️ | ❌ |
Additional transitions for the Sell On Fee feature in DEX V2 contract
Transition Name | Params | Callable when paused? | Callable when not paused? |
---|---|---|---|
WithdrawSellFee | token_address : ByStr20 | ✔️ | ✔️ |
BatchWithdrawSellFees | token_list: List (ByStr20) | ✔️ | ✔️ |
WithdrawSellFeeByRecipient | token_address : ByStr20 | ✔️ | ✔️ |
All the transitions in the contract can be categorized into four categories:
- Housekeeping Transitions: Meant to facilitate non-functional tasks of contract.
- ZRC-2 < > ZRC-2 Transitions: Meant to facilitate basic functional tasks related to trading pair where both sides are ZRC-2 transitions.
- ZIL < > ZRC-2 Transitions: Meant to facilitate basic functional tasks related to trading pair where one side is ZIL and other side is ZRC-2 transitions.
- Upgrade Transitions: The transitions that allows to upgrade and populate mutable fieldswhile upgrading to other implementation of
DEX_Xcad
. - Callback Transitions: The transitions that are callback for standard ZRC2 transitions.
Each of these category of transitions are presented in further detail below.
Name | Description |
---|---|
Pause |
Change state of DEX_Xcad contract to be paused. owner can invoke this transition |
UnPause |
Change state of DEX_Xcad contract to be un-paused. owner can invoke this transition. |
TransferOwnership |
Change ownership of DEX to a new address. owner can invoke this transition |
AcceptPendingOwnership |
Accept ownership of DEX contract. |
TransferProxyOwnership |
Change the current pending_owner of the proxy contract. admin can invoke this transition. |
AcceptPendingProxyOwnership |
Accept ownership of Proxy contract. |
AddTokenPair |
Add token pair to allow swap. owner can invoke this transition |
RemoveTokenPair |
Add token pair to allow swap. owner can invoke this transition |
SetFee |
Set fee for swap. owner can invoke this transition |
SetTreasuryFeesAddress |
Set the designated address for treasury fees withdrawal. owner can invoke this transition |
The following are the public transitions that can be called via smart contract invocations. It involves trading pair where both sides are ZRC-2 tokens.
Each transition has a deadline_block
parameter that can be used to set the block for which the signed transaction is no longer valid be executed by the Zilliqa blockchain.
This can be used to prevent a "transaction withholding attack" by miners, where a transaction can be withheld indefinitely, to only be confirmed when it may benefit other parties in ways unexpected or detrimental to the sender.
This transition adds liquidity to the xpool(pool maintained for xcad LPs) for the ZRC-2 token given by token0_address
and token1_address
. Liquidity providers deposit token0 and token1 ZRC-2 tokens using the exchange rate of the liquidity pool (i.e. the ratios between the two tokens' reserve amounts) at the moment of the transition.
The token0 token0_amount
sent is the exact amount of tokens that the sender wishes to add to the liquidity pool and it should be 50% of the total value that they wish to deposit into the pool.
Because the ratio of tokens in a liquidity pool can fluctutate between when the sender signs the transaction and when it is processed by the blockchain, the parameter bound max_token1_contribution_amount
is used to bound the exchange rate. For the first liquidity provider, max_token1_contribution_amount
is the exact amount of ZRC-2 tokens that will be deposited.
The min_token0_contribution_amount
can be used to set the lower bound of the sender's the contribution share (given by min_token0_contribution_amount/xtotal_contributions[pool_address]
) when the transaction is executed. For the first liquidity provider, min_token0_contribution_amount
is ignored.
Note that liquidity providers should aim to deposit what they believe to be equal values of both token0 and token1 ZRC-2 tokens. While the initial exchange rate is set by the first liquidity provider that creates a pool, arbitrage traders will bring the prices to equilibrium at the expense of the initial liquidity provider(s), should this ratio be irreflective of their true value.
Parameter | Type | Description |
---|---|---|
token0_address | ByStr20 | The token0 token address of the xpool to add liquidity to |
token1_address | ByStr20 | The token1 token address of the xpool to add liquidity to |
_amount |
Uint128 | The amount of ZIL to contribute to the pool, put it as zero |
token0_amount | Uint128 | The amount of token0 to contribute to the xpool |
min_token0_contribution_amount | Uint128 | The minimum liquidity tokens that needs to be minted |
max_token1_contribution_amount | Uint128 | The maximum amount of ZRC-2 token to contribute to the xpool |
deadline_block | BNum | The deadline that this transaction must be executed by |
This transition removes liquidity from the xpool for the ZRC-2 token0,token1 given by token0_address+token1_address
. Liquidity providers can withdraw their share of token0 and token1 tokens based on the exchange rate of the liquidity pool (i.e. the ratios between the two tokens' reserve amounts) at the moment of the transition.
The token0_contribution_amount
can be used to redraw all or some of the sender's tokens based on his previous contributions found in xbalances[token0_address+token1_address][token0_address][_sender]
.
Because the ratio of tokens in a liquidity pool can fluctutate between when the sender signs the transaction and when it is processed by the blockchain, the parameter bounds min_token0_amount
and min_token1_amount
is used to bound the exchange rate.
Parameter | Type | Description |
---|---|---|
token0_address | ByStr20 | The token0 token address of the pool to add liquidity to |
token1_address | ByStr20 | The token1 token address of the pool to add liquidity to |
token0_contribution_amount | Uint128 | The share of contribution to remove |
min_token0_amount | Uint128 | The minimum amount of token0 to be withdrawn |
min_token1_amount | Uint128 | The minimum amount of token1 tokens to be withdrawn |
deadline_block | BNum | The deadline that this transaction must be executed by |
This transition swaps ZRC-2 tokens given by token0_address
for an exact amount of another ZRC-2 token given by token1_address
at the prevailing exchange rate that is determined by the constant product formula of the liquidity pools.
The maximum amount of token0
to be given for the swap is capped by token0_amount
, which can be used to bound the swap exchange rate that may fluctuate from the time of the sender signs the transaction and when it is processed by the blockchain. The transaction reverts with an error RequestedRatesCannotBeFulfilled
if the prevailing exchange rate during transaction execution does not allow this condition to be satisfied.
Parameter | Type | Description |
---|---|---|
token0_address | ByStr20 | The token address of the ZRC-2 token to send (sell) |
token1_address | ByStr20 | The token address of the ZRC-2 token to take (buy) |
token0_amount | Uint128 | The maximum amount of token0 to be sent (sold) |
token1_amount | Uint128 | The exact amount of token1 to be taken (bought) |
deadline_block | BNum | The deadline that this transaction must be executed by |
This transition swaps an exact amount of token0 tokens given by token0_address
for another ZRC-2 token given by token1_address
at the prevailing exchange rate that is determined by the constant product formula of the liquidity pools.
The minimum amount of token1
to be taken in return is given by token1_amount
, which can be used to bound the swap exchange rate that may fluctuate from the time of the sender signs the transaction and when it is processed by the blockchain. The transaction reverts with an error RequestedRatesCannotBeFulfilled
if the prevailing exchange rate during transaction execution does not allow this condition to be satisfied.
Parameter | Type | Description |
---|---|---|
token0_address | ByStr20 | The token address of the ZRC-2 token to send (sell) |
token1_address | ByStr20 | The token address of the ZRC-2 token to take (buy) |
token0_amount | Uint128 | The exact amount of token0 to be sent (sold) |
token1_amount | Uint128 | The minimum amount of token1 to be taken (bought) |
deadline_block | BNum | The deadline that this transaction must be executed by |
The following are the public transitions that can be called via smart contract invocations. It involves trading pair where both one side is ZIL and other side is ZRC-2 token.
Each transition has a deadline_block
parameter that can be used to set the block for which the signed transaction is no longer valid be executed by the Zilliqa blockchain.
This can be used to prevent a "transaction withholding attack" by miners, where a transaction can be withheld indefinitely, to only be confirmed when it may benefit other parties in ways unexpected or detrimental to the sender.
This transition adds liquidity to the pool for the ZRC-2 token given by token_address
. Liquidity providers deposit ZIL and ZRC-2 tokens using the exchange rate of the liquidity pool (i.e. the ratios between the two tokens' reserve amounts) at the moment of the transition.
The ZIL _amount
sent is the exact amount of ZIL that the sender wishes to add to the liquidity pool and it should be 50% of the total value that they wish to deposit into the pool.
Because the ratio of tokens in a liquidity pool can fluctutate between when the sender signs the transaction and when it is processed by the blockchain, the parameter bound max_token_amount
is used to bound the exchange rate. For the first liquidity provider, max_token_amount
is the exact amount of ZRC-2 tokens that will be deposited.
The min_contribution_amount
can be used to set the lower bound of the sender's the contribution share (given by min_contribution_amount/total_contributions[pool_address]
) when the transaction is executed. For the first liquidity provider, min_contribution_amount
is ignored.
Note that liquidity providers should aim to deposit what they believe to be equal values of both ZIL and the ZRC-2 tokens. While the initial exchange rate is set by the first liquidity provider that creates a pool, arbitrage traders will bring the prices to equilibrium at the expense of the initial liquidity provider(s), should this ratio be irreflective of their true value.
Parameter | Type | Description |
---|---|---|
token_address | ByStr20 | The token address of the pool to add liquidity to |
_amount |
Uint128 | The amount of ZIL to contribute to the pool |
min_contribution_amount | ByStr20 | The minimum liquidity tokens that needs to be minted |
max_token_amount | Uint128 | The maximum amount of ZRC-2 token to contribute to the pool |
deadline_block | BNum | The deadline that this transaction must be executed by |
This transition removes liquidity from the pool for the ZRC-2 token given by token_address
. Liquidity providers can withdraw their share of ZIL and ZRC-2 tokens based on the exchange rate of the liquidity pool (i.e. the ratios between the two tokens' reserve amounts) at the moment of the transition.
The contribution_amount
can be used to redraw all or some of the sender's tokens based on his previous contributions found in balances[pool_address][_sender]
.
Because the ratio of tokens in a liquidity pool can fluctutate between when the sender signs the transaction and when it is processed by the blockchain, the parameter bounds min_zil_amount
and min_token_amount
is used to bound the exchange rate.
Parameter | Type | Description |
---|---|---|
token_address | ByStr20 | The token address of the pool to add liquidity to |
contribution_amount | Uint128 | The share of contribution to remove |
min_zil_amount | ByStr20 | The minimum amount of ZIL to be withdrawn |
min_token_amount | Uint128 | The minimum amount of ZRC-2 tokens to be withdrawn |
deadline_block | BNum | The deadline that this transaction must be executed by |
This transition swaps an exact amount of Zilliqa tokens (ZIL) for ZRC-2 tokens given by token_address
at the prevailing exchange rate that is determined by the constant product formula of the liquidity pool.
The minimum amount of ZRC-2 tokens to be taken in return is given by min_token_amount
, which can be used to bound the swap exchange rate that may fluctuate from the time of the sender signs the transaction and when it is processed by the blockchain. The transaction reverts with an error RequestedRatesCannotBeFulfilled
if the prevailing exchange rate during transaction execution does not allow this condition to be satisfied.
Parameter | Type | Description |
---|---|---|
token_address | ByStr20 | The token address of the pool to add liquidity to |
_amount |
Uint128 | The exact amount of ZIL to be sent (sold) |
min_token_amount | Uint128 | The minimum amount of ZRC-2 tokens to be taken (bought) |
deadline_block | BNum | The deadline that this transaction must be executed by |
This transition swaps an exact amount of ZRC-2 tokens given by token_address
for Zilliqa tokens (ZIL) at the prevailing exchange rate that is determined by the constant product formula of the liquidity pool.
The minimum amount of ZIL to be taken in return is given by min_zil_amount
, which can be used to bound the swap exchange rate that may fluctuate from the time of the sender signs the transaction and when it is processed by the blockchain. The transaction reverts with an error RequestedRatesCannotBeFulfilled
if the prevailing exchange rate during transaction execution does not allow this condition to be satisfied.
Parameter | Type | Description |
---|---|---|
token_address | ByStr20 | The token address of the pool to add liquidity to |
token_amount | Uint128 | The exact amount of ZRC-2 tokens to be sent (sold) |
min_zil_amount | Uint128 | The minimum amount of ZIL tokens to be taken (bought) |
deadline_block | BNum | The deadline that this transaction must be executed by |
This transition swaps Zilliqa tokens (ZIL) for an exact amount of ZRC-2 tokens given by token_address
at the prevailing exchange rate that is determined by the constant product formula of the liquidity pool.
The maximum amount of ZIL to be given for the swap is capped by _amount
, which can be used to bound the swap exchange rate that may fluctuate from the time of the sender signs the transaction and when it is processed by the blockchain. The transaction reverts with an error RequestedRatesCannotBeFulfilled
if the prevailing exchange rate during transaction execution does not allow this condition to be satisfied.
Parameter | Type | Description |
---|---|---|
token_address | ByStr20 | The token address of the pool to add liquidity to |
_amount |
Uint128 | The maximum amount of ZIL to be sent (sold) |
token_amount | Uint128 | The exact amount of ZRC-2 tokens to be taken (bought) |
deadline_block | BNum | The deadline that this transaction must be executed by |
This transition swaps ZRC-2 tokens given by token_address
for an exact amount of Zilliqa tokens (ZIL) at the prevailing exchange rate that is determined by the constant product formula of the liquidity pool.
The maximum amount of ZRC-2 tokens to be given for the swap is capped by max_token_amount
, which can be used to bound the swap exchange rate that may fluctuate from the time of the sender signs the transaction and when it is processed by the blockchain. The transaction reverts with an error RequestedRatesCannotBeFulfilled
if the prevailing exchange rate during transaction execution does not allow this condition to be satisfied.
Parameter | Type | Description |
---|---|---|
token_address | ByStr20 | The token address of the pool to add liquidity to |
max_token_amount | Uint128 | The maximum amount of ZRC-2 tokens to be sent (sold) |
zil_amount | Uint128 | The exact amount of ZIL tokens to be taken (bought) |
deadline_block | BNum | The deadline that this transaction must be executed by |
This transition swaps an exact amount of ZRC-2 tokens given by token_address0
for another ZRC-2 token given by token_address1
at the prevailing exchange rate that is determined by the constant product formula of the liquidity pools.
The minimum amount of token1
to be taken in return is given by min_token1_amount
, which can be used to bound the swap exchange rate that may fluctuate from the time of the sender signs the transaction and when it is processed by the blockchain. The transaction reverts with an error RequestedRatesCannotBeFulfilled
if the prevailing exchange rate during transaction execution does not allow this condition to be satisfied.
Parameter | Type | Description |
---|---|---|
token0_address | ByStr20 | The token address of the ZRC-2 token to send (sell) |
token1_address | ByStr20 | The token address of the ZRC-2 token to take (buy) |
token0_amount | Uint128 | The exact amount of token0 to be sent (sold) |
min_token1_amount | Uint128 | The minimum amount of token1 to be taken (bought) |
deadline_block | BNum | The deadline that this transaction must be executed by |
This transition swaps ZRC-2 tokens given by token_address0
for an exact amount of another ZRC-2 token given by token_address1
at the prevailing exchange rate that is determined by the constant product formula of the liquidity pools.
The maximum amount of token0
to be given for the swap is capped by max_token0_amount
, which can be used to bound the swap exchange rate that may fluctuate from the time of the sender signs the transaction and when it is processed by the blockchain. The transaction reverts with an error RequestedRatesCannotBeFulfilled
if the prevailing exchange rate during transaction execution does not allow this condition to be satisfied.
Parameter | Type | Description |
---|---|---|
token0_address | ByStr20 | The token address of the ZRC-2 token to send (sell) |
token1_address | ByStr20 | The token address of the ZRC-2 token to take (buy) |
max_token0_amount | Uint128 | The maximum amount of token0 to be sent (sold) |
token1_amount | Uint128 | The exact amount of token1 to be taken (bought) |
deadline_block | BNum | The deadline that this transaction must be executed by |
Name | Description |
---|---|
UpgradePool |
Callable to upgrade Pool to new implementation. owner can invoke this transition |
UpgradeXPool |
Callable to upgrade XPool to new implementation. owner can invoke this transition |
PopulatePool |
Populate the Pool in new implementation, called on new implementation by old implementation automatically when UpgradePool is called on Proxy contract. owner can invoke this transition |
PopulateXPool |
Populate the XPool in new implementation, called on new implementation by old implementation automatically when UpgradexPool is called on Proxy contract. owner can invoke this transition |
UpgradeBalances |
Callable to upgrade Balances to new implementation. owner can invoke this transition |
UpgradeXBalances |
Callable to upgrade xBalances to new implementation. owner can invoke this transition |
UpgradeTotalContrib |
Callable to upgrade TotalContrib mutable field to new implementation. owner can invoke this transition |
UpgradeXTotalContrib |
Callable to upgrade xtotalContrib mutable field to new implementation. owner can invoke this transition |
TransferZils |
Transfers the ZIL balance from old implementation to new implementation. owner can invoke this transition. |
TransferTokens |
Transfers tokens from old implementation to new implementation in pools. owner can invoke this transition |
TransferTreasuryTokens |
Transfers tokens from old implementation to new implementation in treasury_balances . owner can invoke this transition |
TransferXTokens |
Transfers tokens from old implementation to new implementation in xpools. owner can invoke this transition. |
UpgradeTo |
Change the current implementation address of the DEX_Xcad contract. admin can invoke this transition |
Name | Description |
---|---|
TransferFromSuccessCallBack |
Callable to dex contract when it sends token by invoking TransferFrom transition in Fungible Token contract. |
TransferSuccessCallBack |
Callable to dex contract when it sends token by invoking Transfer transition in Fungible Token contract. |
RecipientAcceptTransferFrom |
Callable to dex contract when it receives token using TransferFrom transition in Fungible Token contract. |
RecipientAcceptTransfer |
Callable to dex contract when it receives token using Transfer transition in Fungible Token contract. |
AddFunds |
Callable to dex contract when it receives ZIL. |
The prevailing exchange rate determined by the constant product formula of the liquidity pool reserves is too unfavourable for the given transition parameters. Ensure that the given parameters are correct, and that enough buffer (slippage allowance) is given to prevent unneccessary reverts. Using amounts that are bounded to be exactly the exchange rate at submission time is unlikely to succeed.
The transaction took too long to confirm and exceeded the given deadline_block
parameter.
Retry the transition with a later deadline_block
.
A transfer of ZRC-2 tokens to the Zilswap smart contract failed.
Ensure that the sender has approved a sufficient amount of token transfers via IncreaseAllowance
.
A parameter is invalid, most likely due to being zero 0
.
Ensure that all transition parameters are valid.
The pool does not exist for swapping of tokens.
Liquidity needs to be added through AddLiquidity
first.
The pool balance does not exist for tokens.
No contributions found for pool token.
The pool size is too large. Retry with smaller amounts.
Address passed for TransferOwnership is current owner.
User is not allowed to accept pending ownership.
Fee passed for SetFee is not valid.
Token0 amount is less than min liquidity required.
Token1 amount is less than min liquidity required.
Token order for Adding Liquidity is invalid.
Token order for Remove Liquidity is invalid.
Zil amount is less than 1000 zils.
A user cannot transfer amount to self.
Official ZRC-2 reference contact and spec can be found here. To meet the requirements of batch token transfer and DEX whitelisting, reference ZRC-2 contract has been modified.
Name | Description |
---|---|
dex_check_enabled |
It stores the current state of dex check value(True/False). True value indicates that dex is enabled. |
dexs |
It stores the addresses of DEX and corresponding True/False values which indicates whether DEX is enabled or disabled. |
pending_owner |
It stores the address of new proposed owner after current_owner invokes TransferOwnership transition. |
Name | Description |
---|---|
ThrowIfDexNotFound |
It throws exception if dex address is not found |
ThrowIfDexIsActive |
It throws exception if dex is active |
ThrowIfDexIsInActive |
It throws exception if dex is inactive |
Name | Description |
---|---|
IncreaseAllowance |
IncreaseAllowance is modified to check for active state of DEX |
DecreaseAllowance |
DecreaseAllowance is modified to check for active state of DEX |
Name | Description |
---|---|
BatchTransfer |
BatchTransfer is added to support token transfer in batch. It accepts List of pair of address and token amount. |
EnableDexCheck |
EnableDexCheck transition enables Dex whitelisting check. current_owner can invoke this transition. |
DisableDexCheck |
DisableDexCheck disables Dex whitelisting check. current_owner can invoke this transition. |
AddDex |
AddDex adds a new dex for whitelisting. Default status is True, dex is whitelisted. current_owner can invoke this transition. |
DisableDex |
DisableDex disables dex. Status is set to False, dex is not allowed to trade token. current_owner can invoke this transition. |
RemoveDex |
RemoveDex removes dex address entry from contract. Dex is not allowed to trade token. current_owner can invoke this transition. |
TransferOwnership |
TransferOwnership adds pending_owner . So that a new owner can claim and become owner of contract. current_owner can invoke this transition. |
AcceptPendingOwnership |
Upon invocation of this transtion pending_owner becomes the current owner of contract. |
Official ZRC-2 reference contact and spec can be found here. This version of the smart contract is an extension on the modified ZRC 2 contract menntioned above. This is to make the contract Mintable, Burnable and Capped.
Name | Description |
---|---|
cap |
This is the maximum number of tokens that can be minted. |
Name | Description |
---|---|
minters |
It stores the addresses users or smart contracts of DEX and corresponding True/False values which indicates whether the address is a Minter i.e. can Mint new tokens. |
Name | Description |
---|---|
AuthorizedMint |
It Mint new tokens if the amount to be minted does not make it exceed the Cap. It throws exception if excced Cap |
AuthorizedBurnIfSufficientBalance |
It burns existing tokens from users balance. It throws exception if user does not have enough in balance |
IsMinter |
It throws exception if the account given is not a Minter |
Name | Description |
---|
Name | Description |
---|---|
Mint |
Mint new tokens to the recipient account. It throws exception if the caller is not a Minter or if the Cap is exceeded |
Burn |
Burn the tokens. It can be calleed by any user to burns its own balance. It throws exception if the user does not have enough tokens |
SetMinter |
Add a new account to the Minters list. Default status is the contract owner, others Minters can be added with this transition. current_owner can invoke this transition. |
RemoveMinter |
Removes an account from the Minters list. current_owner can invoke this transition. |
Official ZRC-2 reference contact and spec can be found here. This version of the smart contract is an extension on the modified ZRC 2 V2 contract menntioned above. This is to make the contract support Sell On Fee.
Name | Description |
---|---|
sell_fee_bps |
It stores the current sell on fee rate. The value is in bps. 1 bps = 0.01%. Default valueis 500bps = 5%. The fee can be set to 0 to effectively turn off sell fee. |
sell_fee_recipient |
It stores the address ofthe account that will receive sellon fees. Default is contract_owner |
Name | Description |
---|---|
RequireValidSellFee |
It throws exception if the fee given is out ot range. The range is 0 and 10000 |
RequireValidDestination |
It throws exception if the address is zero or same as this contract. |
Name | Description |
---|
Name | Description |
---|---|
SetSellFeeBPS |
Set the new sell on fee. The valid range is 0 - 10000. It throws exception if not called by contrat Owner or fee is out of range. current_owner can invoke this transition. |
SetSellFeeRecipient |
Set the new account to receive sell on fees. It throws exception if new recipient is zero address or same as this contractcurrent_owner can invoke this transition. |
Ceres is a graphical user interface that runs different Zilliqa development tools under Docker container. Ceres will take care of Zilliqa tools for scilla contact development. System requirements and installation instructions can be found here.
Document related to tests can be found here
Document related to upgrade can be found here
It was found that Zilswap contract had a major bug. It was missing check for user's contribution in LP and it could potentially drain the pools.
Below code snippet is taken from zilswap contract
transition RemoveLiquidity(
token_address : ByStr20,
contribution_amount : Uint128,
min_zil_amount : Uint128,
min_token_amount : Uint128,
deadline_block : BNum
)
ThrowIfExpired deadline_block;
ThrowIfZero contribution_amount;
ThrowIfZero min_zil_amount;
ThrowIfZero min_token_amount;
token = Token token_address;
maybe_total_contribution <- total_contributions[token_address];
match maybe_total_contribution with
| None =>
e = { _exception : "MissingPool" };
throw e
| Some total_contribution =>
ThrowIfZero total_contribution;
maybe_pool <- pools[token_address];
match maybe_pool with
| None =>
e = { _exception : "MissingPool" };
throw e
| Some pool =>
match pool with
| Pool x y => (* zil reserve, token reserve *)
maybe_zil_amount = frac contribution_amount total_contribution x;
maybe_token_amount = frac contribution_amount total_contribution y;
match maybe_zil_amount with
| None =>
e = { _exception : "IntegerOverflow" };
throw e
| Some zil_amount =>
match maybe_token_amount with
| None =>
e = { _exception : "IntegerOverflow" };
throw e
| Some token_amount =>
within_limits =
let zil_ok = uint128_ge zil_amount min_zil_amount in
let token_ok = uint128_ge token_amount min_token_amount in
andb zil_ok token_ok;
match within_limits with
| False =>
e = { _exception : "RequestedRatesCannotBeFulfilled" };
throw e
| True =>
existing_balance <- balances[token_address][_sender];
match existing_balance with
| None =>
e = { _exception : "MissingBalance" };
throw e
| Some b =>
new_pool =
let new_x = builtin sub x zil_amount in
let new_y = builtin sub y token_amount in
Pool new_x new_y;
is_pool_now_empty = poolEmpty new_pool;
match is_pool_now_empty with
| True =>
(* clear pool entries when there is no more liquidity *)
delete pools[token_address];
delete balances[token_address];
delete total_contributions[token_address]
| False =>
pools[token_address] := new_pool;
new_balance = builtin sub b contribution_amount;
balances[token_address][_sender] := new_balance;
new_total_contribution = builtin sub total_contribution contribution_amount;
total_contributions[token_address] := new_total_contribution
end;
zils_out = Coins zil zil_amount;
tokens_out = Coins token token_amount;
Send zils_out _sender;
Send tokens_out _sender;
e = { _eventname: "Burnt"; pool: token_address; address: _sender; amount: contribution_amount };
event e
end
end
end
end
end
end
end
end
In the above transition refer to the section,
new_pool =
let new_x = builtin sub x zil_amount in
let new_y = builtin sub y token_amount in
Pool new_x new_y;
is_pool_now_empty = poolEmpty new_pool;
match is_pool_now_empty with
| True =>
(* clear pool entries when there is no more liquidity *)
delete pools[token_address];
delete balances[token_address];
delete total_contributions[token_address]
| False =>
pools[token_address] := new_pool;
new_balance = builtin sub b contribution_amount;
balances[token_address][_sender] := new_balance;
new_total_contribution = builtin sub total_contribution contribution_amount;
total_contributions[token_address] := new_total_contribution
end;
It is missing check for contribution_amount
. User can provide the contribution_amount
which could possibly drain the pool and entire pool tokens can be transferred to his wallet.
Fix is to move new_balance and new_total_contribution calculation up so that it would fail default with exception if user's contribution amount exceeds his balance.
new_balance = builtin sub b contribution_amount;
new_total_contribution = builtin sub total_contribution contribution_amount;
new_pool =
let new_x = builtin sub x zil_amount in
let new_y = builtin sub y token_amount in
Pool new_x new_y;
is_pool_now_empty = poolEmpty new_pool;
match is_pool_now_empty with
| True =>
(* clear pool entries when there is no more liquidity *)
delete pools[token_address];
delete balances[token_address];
delete total_contributions[token_address]
| False =>
pools[token_address] := new_pool;
balances[token_address][_sender] := new_balance;
total_contributions[token_address] := new_total_contribution
end;
This fix has been applied for remove liquidity in zil<>zrc2 as well as zrc2<>zrc2 pools.