Add a new swappable synth. This method is callable by anyone, however _pool must exist within the Curve pool registry and _synth must be a valid synth that is swappable within the pool.
Input
Type
Description
_synth
address
Address of the synth
_pool
address
Address of Curve pool containing the synth
Emits: NewSynth
Source code
@externaldefadd_synth(_synth:address,_pool:address):""" @notice Add a new swappable synth @dev Callable by anyone, however `_pool` must exist within the Curve pool registry and `_synth` must be a valid synth that is swappable within the pool @param _synth Address of the synth to add @param _pool Address of the Curve pool where `_synth` is swappable """assertself.synth_pools[_synth]==ZERO_ADDRESS# dev: already added# this will revert if `_synth` is not actually a synthself.currency_keys[_synth]=Synth(_synth).currencyKey()registry:address=AddressProvider(ADDRESS_PROVIDER).get_registry()pool_coins:address[8]=Registry(registry).get_coins(_pool)has_synth:bool=Falseforcoininpool_coins:ifcoin==ZERO_ADDRESS:asserthas_synth# dev: synth not in poolbreakifcoin==_synth:self.synth_pools[_synth]=_poolhas_synth=Trueself.swappable_synth[coin]=_synthlogNewSynth(_synth,_pool)
Get the address of the Curve pool used to swap a synthetic asset. If this function returns ZERO_ADDRESS, the given synth cannot be used within cross-asset swaps.
Input
Type
Description
_synth
address
Address of the synth
Source code
# synth -> curve pool where it can be tradedsynth_pools:public(HashMap[address,address])...
Get the address of the synthetic asset that _token may be directly swapped for. If this function returns ZERO_ADDRESS, _token cannot be used within a cross-asset swap.
Input
Type
Description
_token
address
Address of the synth
Source code
# synth -> curve pool where it can be tradedsynth_pools:public(HashMap[address,address])...
Returns the expected amount of _synth received in the swap.
Input
Type
Description
_from
address
Address of the initial asset being exchanged
_synth
address
Address of the synth being swapped into
_amount
uint256
Amount of _from to swap
Source code
@view@internaldef_get_swap_into(_from:address,_synth:address,_amount:uint256)->uint256:registry:address=AddressProvider(ADDRESS_PROVIDER).get_registry()intermediate_synth:address=self.swappable_synth[_from]pool:address=self.synth_pools[intermediate_synth]synth_amount:uint256=_amountif_from!=intermediate_synth:i:int128=0j:int128=0i,j=Registry(registry).get_coin_indices(pool,_from,intermediate_synth)synth_amount=Curve(pool).get_dy(i,j,_amount)returnself.exchanger.getAmountsForExchange(synth_amount,self.currency_keys[intermediate_synth],self.currency_keys[_synth],)[0]@view@externaldefget_swap_into_synth_amount(_from:address,_synth:address,_amount:uint256)->uint256:""" @notice Return the amount received when performing a cross-asset swap @dev Used to calculate `_expected` when calling `swap_into_synth`. Be sure to reduce the value slightly to account for market movement prior to the transaction confirmation. @param _from Address of the initial asset being exchanged @param _synth Address of the synth being swapped into @param _amount Amount of `_from` to swap @return uint256 Expected amount of `_synth` received """returnself._get_swap_into(_from,_synth,_amount)
This method is used to calculate _expected when calling swap_into_synth. You should reduce the value slightly to account for market movement prior to the transaction confirming.
Returns the expected amount of _to received in the swap.
Input
Type
Description
_synth
address
Address of the synth being swapped out of
_to
address
Address of the asset to swap into
_amount
uint256
Amount of _synth to swap
Source code
@view@internaldef_get_swap_from(_synth:address,_to:address,_amount:uint256)->uint256:registry:address=AddressProvider(ADDRESS_PROVIDER).get_registry()pool:address=self.synth_pools[_synth]i:int128=0j:int128=0i,j=Registry(registry).get_coin_indices(pool,_synth,_to)returnCurve(pool).get_dy(i,j,_amount)@view@externaldefget_swap_from_synth_amount(_synth:address,_to:address,_amount:uint256)->uint256:""" @notice Return the amount received when swapping out of a settled synth @dev Used to calculate `_expected` when calling `swap_from_synth`. Be sure to reduce the value slightly to account for market movement prior to the transaction confirmation. @param _synth Address of the synth being swapped out of @param _to Address of the asset to swap into @param _amount Amount of `_synth` being exchanged @return uint256 Expected amount of `_to` received """returnself._get_swap_from(_synth,_to,_amount)
Estimate the final amount of _to received when swapping between _from and _to.
Input
Type
Description
_from
address
Address of the initial asset being exchanged
_to
address
Address of the asset to swap into
_amount
uint256
Amount of _from to swap
Source code
@view@internaldef_get_swap_into(_from:address,_synth:address,_amount:uint256)->uint256:registry:address=AddressProvider(ADDRESS_PROVIDER).get_registry()intermediate_synth:address=self.swappable_synth[_from]pool:address=self.synth_pools[intermediate_synth]synth_amount:uint256=_amountif_from!=intermediate_synth:i:int128=0j:int128=0i,j=Registry(registry).get_coin_indices(pool,_from,intermediate_synth)synth_amount=Curve(pool).get_dy(i,j,_amount)returnself.exchanger.getAmountsForExchange(synth_amount,self.currency_keys[intermediate_synth],self.currency_keys[_synth],)[0]...@view@internaldef_get_swap_from(_synth:address,_to:address,_amount:uint256)->uint256:registry:address=AddressProvider(ADDRESS_PROVIDER).get_registry()pool:address=self.synth_pools[_synth]i:int128=0j:int128=0i,j=Registry(registry).get_coin_indices(pool,_synth,_to)returnCurve(pool).get_dy(i,j,_amount)...@view@externaldefget_estimated_swap_amount(_from:address,_to:address,_amount:uint256)->uint256:""" @notice Estimate the final amount received when swapping between `_from` and `_to` @dev Actual received amount may be different if synth rates change during settlement @param _from Address of the initial asset being exchanged @param _to Address of the asset to swap into @param _amount Amount of `_from` being exchanged @return uint256 Estimated amount of `_to` received """synth:address=self.swappable_synth[_to]synth_amount:uint256=self._get_swap_into(_from,synth,_amount)returnself._get_swap_from(synth,_to,synth_amount)
This method is for estimating the received amount from a complete swap over two transactions. If _to is a Synth, you should use get_swap_into_synth_amount instead.
Note
As swaps take a settlement period into account, the actual received amount may be different due to rate changes during the settlement period.
Perform a cross-asset swap between _from and _synth. Returns the uint256 token ID of the NFT representing the unsettled swap. The token ID is also available from the emitted TokenUpdate event.
Input
Type
Description
_from
address
Address of the initial asset being exchanged. For Ether swaps, use 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE.
_synth
address
Address of the synth to swap into
_amount
uint256
Amount of _from to swap. If you are swapping from Ether, you must also send exactly this much Ether with the transaction. If you are swapping any other asset, you must have given approval to the swap contract to transfer at least this amount.
_expected
uint256
Minimum amount of _synth to receive
_receiver
address
Address of the recipient of _synth. Defaults to the msg.sender.
_existing_token_id
uint256
Token ID to deposit _synth into. If not given, a new NFT is minted for the generated synth. When set as non-zero, the token ID must be owned by the caller and must already represent the same synth as is being swapped into.
Emits: NewSettler Transfer TokenUpdate
Source code
@payable@externaldefswap_into_synth(_from:address,_synth:address,_amount:uint256,_expected:uint256,_receiver:address=msg.sender,_existing_token_id:uint256=0,)->uint256:""" @notice Perform a cross-asset swap between `_from` and `_synth` @dev Synth swaps require a settlement time to complete and so the newly generated synth cannot immediately be transferred onward. Calling this function mints an NFT which represents ownership of the generated synth. Once the settlement time has passed, the owner may claim the synth by calling to `swap_from_synth` or `withdraw`. @param _from Address of the initial asset being exchanged @param _synth Address of the synth being swapped into @param _amount Amount of `_from` to swap @param _expected Minimum amount of `_synth` to receive @param _receiver Address of the recipient of `_synth`, if not given defaults to `msg.sender` @param _existing_token_id Token ID to deposit `_synth` into. If left as 0, a new NFT is minted for the generated synth. If non-zero, the token ID must be owned by `msg.sender` and must represent the same synth as is being swapped into. @return uint256 NFT token ID """settler:address=ZERO_ADDRESStoken_id:uint256=0if_existing_token_id==0:# if no token ID is given we are initiating a new swapcount:uint256=self.id_countifcount==0:# if there are no availale settler contracts we must deploy a new onesettler=create_forwarder_to(self.settler_implementation)Settler(settler).initialize()token_id=convert(settler,uint256)logNewSettler(settler)else:count-=1token_id=self.available_token_ids[count]settler=convert(token_id%(2**160),address)self.id_count=countelse:# if a token ID is given we are adding to the balance of an existing swap# so must check to make sure this is a permitted actionsettler=convert(_existing_token_id%(2**160),address)token_id=_existing_token_idowner:address=self.id_to_owner[_existing_token_id]ifmsg.sender!=owner:assertowner!=ZERO_ADDRESS,"Unknown Token ID"assert(self.owner_to_operators[owner][msg.sender]ormsg.sender==self.id_to_approval[_existing_token_id]),"Caller is not owner or operator"assertowner==_receiver,"Receiver is not owner"assertSettler(settler).synth()==_synth,"Incorrect synth for Token ID"registry_swap:address=AddressProvider(ADDRESS_PROVIDER).get_address(2)intermediate_synth:address=self.swappable_synth[_from]synth_amount:uint256=0ifintermediate_synth==_from:# if `_from` is already a synth, no initial curve exchange is requiredassertERC20(_from).transferFrom(msg.sender,settler,_amount)synth_amount=_amountelse:if_from!=0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE:# Vyper equivalent of SafeERC20Transfer, handles most ERC20 return valuesresponse:Bytes[32]=raw_call(_from,concat(method_id("transferFrom(address,address,uint256)"),convert(msg.sender,bytes32),convert(self,bytes32),convert(_amount,bytes32),),max_outsize=32,)iflen(response)!=0:assertconvert(response,bool)ifnotself.is_approved[_from][registry_swap]:response=raw_call(_from,concat(method_id("approve(address,uint256)"),convert(registry_swap,bytes32),convert(MAX_UINT256,bytes32),),max_outsize=32,)iflen(response)!=0:assertconvert(response,bool)self.is_approved[_from][registry_swap]=True# use Curve to exchange for initial synth, which is sent to the settlersynth_amount=RegistrySwap(registry_swap).exchange(self.synth_pools[intermediate_synth],_from,intermediate_synth,_amount,0,settler,value=msg.value)# use Synthetix to convert initial synth into the target synthinitial_balance:uint256=ERC20(_synth).balanceOf(settler)Settler(settler).convert_synth(_synth,synth_amount,self.currency_keys[intermediate_synth],self.currency_keys[_synth])final_balance:uint256=ERC20(_synth).balanceOf(settler)assertfinal_balance-initial_balance>=_expected,"Rekt by slippage"# if this is a new swap, mint an NFT to represent the unsettled conversionif_existing_token_id==0:self.id_to_owner[token_id]=_receiverself.owner_to_token_count[_receiver]+=1logTransfer(ZERO_ADDRESS,_receiver,token_id)logTokenUpdate(token_id,_receiver,_synth,final_balance)returntoken_id
Synth swaps require a settlement time to complete and so the newly generated synth cannot immediately be transferred onward. Calling this function mints an NFT representing ownership of the unsettled synth.
Get information about the underlying synth represented by an NFT.
Returns:
the address of the owner of the NFT
the address of the underlying synth
the balance (uint256) of the underlying synth
the current maximum number of seconds until the synth may be settled (uint256)
Input
Type
Description
_token_id
uint256
NFT token ID to query info about. Reverts if the token ID does not exist.
Source code
@view@externaldeftoken_info(_token_id:uint256)->TokenInfo:""" @notice Get information about the synth represented by an NFT @param _token_id NFT token ID to query info about @return NFT owner Address of synth within the NFT Balance of the synth Max settlement time in seconds """info:TokenInfo=empty(TokenInfo)info.owner=self.id_to_owner[_token_id]assertinfo.owner!=ZERO_ADDRESSsettler:address=convert(_token_id%(2**160),address)info.synth=Settler(settler).synth()info.underlying_balance=ERC20(info.synth).balanceOf(settler)ifnotself.is_settled[_token_id]:currency_key:bytes32=self.currency_keys[info.synth]reclaim:uint256=0rebate:uint256=0reclaim,rebate=self.exchanger.settlementOwing(settler,currency_key)info.underlying_balance=info.underlying_balance-reclaim+rebateinfo.time_to_settle=self.exchanger.maxSecsLeftInWaitingPeriod(settler,currency_key)returninfo
Swap the underlying synth represented by an NFT into another asset. Callable by the owner or operator of _token_id after the synth settlement period has passed. If _amount is equal to the total remaining balance of the synth represented by the NFT, the NFT is burned.
Returns the remaining balance of the underlying synth within the active NFT.
Input
Type
Description
_token_id
uint256
The identifier for an NFT
_to
address
Address of the asset to swap into
_amount
uint256
Amount of the underlying synth to swap
_expected
uint256
Minimum amount of _to to receive
_receiver
address
Address to send the final received asset to. Defaults to msg.sender.
Emits: Transfer TokenUpdate
Source code
@externaldefswap_from_synth(_token_id:uint256,_to:address,_amount:uint256,_expected:uint256,_receiver:address=msg.sender,)->uint256:""" @notice Swap the synth represented by an NFT into another asset. @dev Callable by the owner or operator of `_token_id` after the synth settlement period has passed. If `_amount` is equal to the entire balance within the NFT, the NFT is burned. @param _token_id The identifier for an NFT @param _to Address of the asset to swap into @param _amount Amount of the synth to swap @param _expected Minimum amount of `_to` to receive @param _receiver Address of the recipient of the synth, if not given defaults to `msg.sender` @return uint256 Synth balance remaining in `_token_id` """owner:address=self.id_to_owner[_token_id]ifmsg.sender!=self.id_to_owner[_token_id]:assertowner!=ZERO_ADDRESS,"Unknown Token ID"assert(self.owner_to_operators[owner][msg.sender]ormsg.sender==self.id_to_approval[_token_id]),"Caller is not owner or operator"settler:address=convert(_token_id%(2**160),address)synth:address=self.swappable_synth[_to]pool:address=self.synth_pools[synth]# ensure the synth is settled prior to swappingifnotself.is_settled[_token_id]:currency_key:bytes32=self.currency_keys[synth]self.exchanger.settle(settler,currency_key)self.is_settled[_token_id]=True# use Curve to exchange the synth for another asset which is sent to the receiverremaining:uint256=Settler(settler).exchange(_to,pool,_amount,_expected,_receiver)# if the balance of the synth within the NFT is now zero, burn the NFTifremaining==0:self.id_to_owner[_token_id]=ZERO_ADDRESSself.id_to_approval[_token_id]=ZERO_ADDRESSself.is_settled[_token_id]=Falseself.owner_to_token_count[msg.sender]-=1count:uint256=self.id_count# add 2**160 to increment the nonce for next time this settler is usedself.available_token_ids[count]=_token_id+2**160self.id_count=count+1owner=ZERO_ADDRESSsynth=ZERO_ADDRESSlogTransfer(msg.sender,ZERO_ADDRESS,_token_id)logTokenUpdate(_token_id,owner,synth,remaining)returnremaining
Withdraw the underlying synth represented by an NFT. Callable by the owner or operator of _token_id after the synth settlement period has passed. If _amount is equal to the total remaining balance of the synth represented by the NFT, the NFT is burned.
Returns the remaining balance of the underlying synth within the active NFT.
Input
Type
Description
_token_id
uint256
The identifier for an NFT
_amount
uint256
Amount of the underlying synth to swap
_receiver
address
Address of the recipient of the withdrawn synth. Defaults to the msg.sender.
Emits: Transfer TokenUpdate
Source code
@externaldefwithdraw(_token_id:uint256,_amount:uint256,_receiver:address=msg.sender)->uint256:""" @notice Withdraw the synth represented by an NFT. @dev Callable by the owner or operator of `_token_id` after the synth settlement period has passed. If `_amount` is equal to the entire balance within the NFT, the NFT is burned. @param _token_id The identifier for an NFT @param _amount Amount of the synth to withdraw @param _receiver Address of the recipient of the synth, if not given defaults to `msg.sender` @return uint256 Synth balance remaining in `_token_id` """owner:address=self.id_to_owner[_token_id]ifmsg.sender!=self.id_to_owner[_token_id]:assertowner!=ZERO_ADDRESS,"Unknown Token ID"assert(self.owner_to_operators[owner][msg.sender]ormsg.sender==self.id_to_approval[_token_id]),"Caller is not owner or operator"settler:address=convert(_token_id%(2**160),address)synth:address=Settler(settler).synth()# ensure the synth is settled prior to withdrawalifnotself.is_settled[_token_id]:currency_key:bytes32=self.currency_keys[synth]self.exchanger.settle(settler,currency_key)self.is_settled[_token_id]=Trueremaining:uint256=Settler(settler).withdraw(_receiver,_amount)# if the balance of the synth within the NFT is now zero, burn the NFTifremaining==0:self.id_to_owner[_token_id]=ZERO_ADDRESSself.id_to_approval[_token_id]=ZERO_ADDRESSself.is_settled[_token_id]=Falseself.owner_to_token_count[msg.sender]-=1count:uint256=self.id_count# add 2**160 to increment the nonce for next time this settler is usedself.available_token_ids[count]=_token_id+2**160self.id_count=count+1owner=ZERO_ADDRESSsynth=ZERO_ADDRESSlogTransfer(msg.sender,ZERO_ADDRESS,_token_id)logTokenUpdate(_token_id,owner,synth,remaining)returnremaining
Settle the synth represented in an NFT. Note that settlement is performed when swapping or withdrawing, there is no requirement to call this function separately. Returns True.
Input
Type
Description
_token_id
uint256
The identifier for an NFT
Source code
@externaldefsettle(_token_id:uint256)->bool:""" @notice Settle the synth represented in an NFT. @dev Settlement is performed when swapping or withdrawing, there is no requirement to call this function separately. @param _token_id The identifier for an NFT @return bool Success """ifnotself.is_settled[_token_id]:assertself.id_to_owner[_token_id]!=ZERO_ADDRESS,"Unknown Token ID"settler:address=convert(_token_id%(2**160),address)synth:address=Settler(settler).synth()currency_key:bytes32=self.currency_keys[synth]self.exchanger.settle(settler,currency_key)# dev: settlement failedself.is_settled[_token_id]=TruereturnTrue