SIP-37 in the Achernar release introduced Fee Reclamation into Synthetix. This means, following all exchanges into a synth, a waiting period must expire before subsequent exchanges out of that synth can be processed.
Once the waiting period expires, settlement is performed automatically during subsequent exchanges. However, subsequent transfer attempts will always fail if there is not sufficient balance after settlement, hence transferAndSettle must be used.
Transfer and settle
It was decided during SIP-37 to not automatically settle within transfers. The reason being is that to settle within a transfer may break ERC20 conventions because the amount provided as a parameter might not be the amount emitted via Transfer due to fees owing or owed.
Note: Synthetix uses a proxy system. The ABI of the underlying Synthetix ProxyERC20 contract you need is Synthetix. Learn more about how proxies work by visiting the overview page.
const{SynthetixJs}=require('synthetix-js');constprivateKey='0x'+'1'.repeat(64);// don't actually put a private key in code obviously// parameters: default provider, default networkId, private key as a stringconstnetworkId=11155111;// sepolia, (use 1 for mainnet)constsigner=newSynthetixJs.signers.PrivateKey(null,networkId,privateKey);constsnxjs=newSynthetixJs({signer,networkId});const{toUtf8Bytes32,parseEther}=snxjs.utils;(async()=>{try{// send transactionconsttxn=awaitsnxjs.Synthetix.settle(toUtf8Bytes32('iETH'));console.log('hash is mining',txn.hash);// wait for miningawaittxn.wait();// fetch logs of transactionconst{logs}=awaitsigner.provider.getTransactionReceipt(txn.hash);// show themconsole.log(JSON.stringify(logs,null,'\t'));}catch(err){console.log('Error',err);}})();
constsynthetix=require('synthetix');// nodejsconstethers=require('ethers');// nodejs// or using ES modules:// import synthetix from 'synthetix';// import ethers from 'ethers';constnetwork='sepolia';constprovider=ethers.getDefaultProvider(network==='mainnet'?'homestead':network);const{address}=synthetix.getTarget({network,contract:'ProxyERC20'});const{abi}=synthetix.getSource({network,contract:'Synthetix'});constprivateKey='0x'+'1'.repeat(64);// don't actually put a private key in code obviouslyconstsigner=newethers.Wallet(privateKey).connect(provider);// see https://docs.ethers.io/ethers.js/html/api-contract.html#connecting-to-existing-contractsconstSynthetix=newethers.Contract(address,abi,signer);const{toBytes32}=synthetix;(async()=>{try{// send transactionconsttxn=awaitSynthetix.settle(toBytes32('iETH'));// wait for miningawaittxn.wait();// fetch logs of transactionconst{logs}=awaitprovider.getTransactionReceipt(txn.hash);// displayconsole.log(JSON.stringify(logs,null,'\t'));}catch(err){console.log('Error',err);}})();
pragma solidity0.5.16;import"synthetix/contracts/interfaces/IAddressResolver.sol";import"synthetix/contracts/interfaces/ISynthetix.sol";import"synthetix/contracts/interfaces/IExchanger.sol";contractMyContract{// This should be instantiated with our ReadProxyAddressResolver// it's a ReadProxy that won't change, so safe to code it here without a setter// see https://docs.synthetix.io/addresses for addresses in mainnet and testnetsIAddressResolverpublicsynthetixResolver;constructor(IAddressResolver_snxResolver)public{synthetixResolver=_snxResolver;}functionsynthetixSettle(bytes32synthKey)external{)ISynthetixsynthetix=synthetixResolver.getAddress("Synthetix");require(synthetix!=address(0),"Synthetix is missing from Synthetix resolver");// This check is what synthetix.exchange() will perform, added here for explicitnessrequire(!synthetix.isWaitingPeriod(synthKey),"Cannot settle during the waiting period");// Settle for msg.sender = address(MyContract)synthetix.settle(synthKey);}functionsynthetixSettleOnBehalf(addressuser,bytes32synthKey)external{IExchangerexchanger=synthetixResolver.getAddress("Exchanger");require(exchanger!=address(0),"Exchanger is missing from Synthetix resolver");// This check is what exchanger.settle() will perform, added here for explicitnessrequire(exchanger.maxSecsLeftInWaitingPeriod(user,synthKey)==0,"Cannot settle during the waiting period");// This function has no msg.sender restriction - any address can call it (they'll just have to pay the gas on behalf of the user)exchanger.settle(user,synthKey)}functionsynthetixTransferAndSettle(bytes32synthKey,addressto,uintvalue)extenrnal{// Note ⚠️: IAddressResolver.getSynth will not work until the Altair release (v2.22) of SynthetixISynthsynth=synthetixResolver.getSynth(synthKey);require(synth!=address(0),"Synth is missing from Synthetix");synth.transferAndSettle(to,value);}functionsynthetixTransferFromAndSettle(bytes32synthKey,addressfrom,addressto,uintvalue)extenrnal{// Note ⚠️: IAddressResolver.getSynth will not work until the Altair release (v2.22) of SynthetixISynthsynth=synthetixResolver.getSynth(synthKey);require(synth!=address(0),"Synth is missing from Synthetix");// Note: only works if user has invoked ERC20.approve(address(MyContract)) on the given synthsynth.transferFromAndSettle(from,to,value);}}