Skip to content

Unstaking: Closing a Position

In order to close their position, SNX stakers need to burn enough sUSD to cover their debt position (Synthetix.debtBalanceOf(user, "sUSD")).

Burning API

Contract

Destination contract (address): ProxyERC20

Target contract (ABI): Synthetix

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.

Methods

Events Emitted

On a successful transaction, the following events occur:

name emitted on address from address to uint value
Transfer ProxysUSD msg.sender (or user) 0x0 amount of sUSD
name emitted on address account uint value
Burned ProxysUSD msg.sender (or user) amount
name emitted on address account uint debtRatio uint debtEntryIndex uint feePeriodStartingDebtIndex
IssuanceDebtRatioEntry FeePool msg.sender (or user) debtRatio debtEntryIndex feePeriodStartingDebtIndex

Examples from Mainnet

  • ProxySynthetix.burnSynths(3e18)

  • ProxySynthetix.burnSynthsToTargetOnBehalf(0x3bf10de)


Code Snippets

Unstaking (burning)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
const { SynthetixJs } = require('synthetix-js');
const privateKey = '0x' + '1'.repeat(64); // don't actually put a private key in code obviously

// parameters: default provider, default networkId, private key as a string
const networkId = 11155111; // sepolia, (use 1 for mainnet)
const signer = new SynthetixJs.signers.PrivateKey(null, networkId, privateKey);
const snxjs = new SynthetixJs({ signer, networkId });

(async () => {
    try {
        // get debt owing
        const debt = await snxjs.Synthetix.debtBalanceOf(signer.address, snxjs.utils.toUtf8Bytes32('sUSD'));

        // burn all debt owing
        const txn = await snxjs.Synthetix.burnSynths(debt);

        console.log('hash is mining', txn.hash);

        // wait for mining
        await txn.wait();

        // fetch logs of transaction
        const { logs } = await signer.provider.getTransactionReceipt(txn.hash);

        // show them
        console.log(JSON.stringify(logs, null, '\t'));
    } catch (err) {
        console.log('Error', err);
    }
})();
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
const synthetix = require('synthetix'); // nodejs
const ethers = require('ethers'); // nodejs
// or using ES modules:
// import synthetix from 'synthetix';
// import ethers from 'ethers';

const network = 'sepolia';
const provider = ethers.getDefaultProvider(network === 'mainnet' ? 'homestead' : network);

const { address } = synthetix.getTarget({ network, contract: 'ProxyERC20' });
const { abi } = synthetix.getSource({ network, contract: 'Synthetix' });

const privateKey = '0x' + '1'.repeat(64); // don't actually put a private key in code obviously
const signer = new ethers.Wallet(privateKey).connect(provider);

// see https://docs.ethers.io/ethers.js/html/api-contract.html#connecting-to-existing-contracts
const Synthetix = new ethers.Contract(address, abi, signer);

(async () => {
try {

    const debt = await Synthetix.debtBalanceOf(signer.address, synthetix.toBytes32('sUSD'));

    // burn all debt owing
    const txn = await Synthetix.burnSynths(debt);

    // wait for mining
    await txn.wait();
    // fetch logs of transaction
    const { logs } = await provider.getTransactionReceipt(txn.hash);
    // display
    console.log(JSON.stringify(logs, null, '\t'));
} catch (err) {
    console.log('Error', err);
}
})();
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
pragma solidity 0.5.16;

import "synthetix/contracts/interfaces/IAddressResolver.sol";
import "synthetix/contracts/interfaces/ISynthetix.sol";


contract MyContract {

    // 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 testnets
    IAddressResolver public synthetixResolver;

    constructor(IAddressResolver _snxResolver) public {
        synthetixResolver = _snxResolver;
    }

    function synthetixBurn() external {
        ISynthetix synthetix = synthetixResolver.getAddress("Synthetix");
        require(synthetix != address(0), "Synthetix is missing from Synthetix resolver");

        uint debt = synthetix.debtBalanceOf(msg.sender, "sUSD");

        // Burn for msg.sender = address(MyContract)
        synthetix.burnSynths(debt);
    }

    function synthetixBurnOnBehalf(address user) external {
        ISynthetix synthetix = synthetixResolver.getAddress("Synthetix");
        require(synthetix != address(0), "Synthetix is missing from Synthetix resolver");

        uint debt = synthetix.debtBalanceOf(user, "sUSD");

        // Note: this will fail if `DelegateApprovals.approveBurnOnBehalf(address(MyContract))` has
        // not yet been invoked by the `user`
        synthetix.burnSynthsOnBehalf(user, debt);
    }
}