Using npm:
npm install @buildwithsygma/sygma-sdk-core
Using yarn:
yarn add @buildwithsygma/sygma-sdk-core
Sygma SDK is an OpenSource (under GNU Lesser General Public License v3.0) SDK for developers to work with Sygma bridge. The SDK consist of methods that enable bridging capabilities between Ethereum networks.
NOTE the SDK is under an active development so can be broken occasionally.
The current SDK has one package that comprises the whole bridging logic for transferring ERC20 tokens between Ethereum networks. Alongside this there are two folder examples that demonstrate the usage of our SDK. If you want to run the examples alongside our bridging infrastructure, please make sure you have Sygma bridge in order for you to run make example
command.
For React example, after you have run and deployed the contracts using Sygma bridge, go to the examples folder and follow the instructions in their README
In order for you to use our SDK, first you need to clone Sygma bridge repository to your local machine. After that, follow the instructions to run the local example. It will use docker and docker-compose to run 2 local RPC node and 2 relayers.
# inside the root directory of Sygma project
make example
show example of the log from local setup
You should see something like this after the local setup has all the dependencies runningAttaching to evm1-1, evm2-1, example_relayer1, example_relayer2, example_relayer3, fee-oracle
evm1-1 | Ganache CLI v6.12.2 (ganache-core: 2.13.2)
evm1-1 | (node:1) [DEP0005] DeprecationWarning: Buffer() is deprecated due to security and usability issues. Please use the Buffer.alloc(), Buffer.allocUnsafe(), or Buffer.from() methods instead.
evm1-1 | (Use `node --trace-deprecation ...` to show where the warning was created)
evm1-1 |
evm1-1 | Available Accounts
evm1-1 | ==================
evm1-1 | (0) 0x90F8bf6A479f320ead074411a4B0e7944Ea8c9C1 (100 ETH)
evm1-1 | (1) 0xFFcf8FDEE72ac11b5c542428B35EEF5769C409f0 (100 ETH)
evm1-1 | (2) 0x22d491Bde2303f2f43325b2108D26f1eAbA1e32b (100 ETH)
evm1-1 | (3) 0xE11BA2b4D45Eaed5996Cd0823791E0C93114882d (100 ETH)
evm1-1 | (4) 0xd03ea8624C8C5987235048901fB614fDcA89b117 (100 ETH)
evm1-1 | (5) 0x95cED938F7991cd0dFcb48F0a06a40FA1aF46EBC (100 ETH)
evm1-1 | (6) 0x3E5e9111Ae8eB78Fe1CC3bb8915d5D461F3Ef9A9 (100 ETH)
evm1-1 | (7) 0x28a8746e75304c0780E011BEd21C72cD78cd535E (100 ETH)
evm1-1 | (8) 0xACa94ef8bD5ffEE41947b4585a84BdA5a3d3DA6E (100 ETH)
evm1-1 | (9) 0x1dF62f291b2E969fB0849d99D9Ce41e2F137006e (100 ETH)
evm1-1 |
evm1-1 | Private Keys
evm1-1 | ==================
evm1-1 | (0) 0x4f3edf983ac636a65a842ce7c78d9aa706d3b113bce9c46f30d7d21715b23b1d
evm1-1 | (1) 0x6cbed15c793ce57650b9877cf6fa156fbef513c4e6134f022a85b1ffdd59b2a1
evm1-1 | (2) 0x6370fd033278c143179d81c5526140625662b8daa446c22ee2d73db3707e620c
evm1-1 | (3) 0x646f1ce2fdad0e6deeeb5c7e8e5543bdde65e86029e2fd9fc169899c440a7913
evm1-1 | (4) 0xadd53f9a7e588d003326d1cbf9e4a43c061aadd9bc938c843a79e7b4fd2ad743
evm1-1 | (5) 0x395df67f0c2d2d9fe1ad08d1bc8b6627011959b79c53d7dd6a3536a33ab8a4fd
evm1-1 | (6) 0xe485d098507f54e7733a205420dfddbe58db035fa577fc294ebd14db90767a52
evm1-1 | (7) 0xa453611d9419d0e56f499079478fd72c37b251a94bfde4d19872c44cf65386e3
evm1-1 | (8) 0x829e924fdf021ba3dbbc4225edfece9aca04b929d6e75613329ca6f1d31c0bb4
evm1-1 | (9) 0xb0057716d5917badaf911b193b12b910811c1497b5bada8d7711f758981c3773
evm1-1 |
evm1-1 | HD Wallet
evm1-1 | ==================
evm1-1 | Mnemonic: myth like bonus scare over problem client lizard pioneer submit female collect
evm1-1 | Base HD Path: m/44'/60'/0'/0/{account_index}
evm1-1 |
evm1-1 | Gas Price
evm1-1 | ==================
evm1-1 | 20000000000
evm1-1 |
evm1-1 | Gas Limit
evm1-1 | ==================
evm1-1 | 6721975
evm1-1 |
evm1-1 | Call Gas Limit
evm1-1 | ==================
evm1-1 | 9007199254740991
evm1-1 |
evm1-1 | Listening on 0.0.0.0:8545
evm2-1 | Ganache CLI v6.12.2 (ganache-core: 2.13.2)
evm2-1 | (node:1) [DEP0005] DeprecationWarning: Buffer() is deprecated due to security and usability issues. Please use the Buffer.alloc(), Buffer.allocUnsafe(), or Buffer.from() methods instead.
evm2-1 | (Use `node --trace-deprecation ...` to show where the warning was created)
evm2-1 |
evm2-1 | Available Accounts
evm2-1 | ==================
evm2-1 | (0) 0x90F8bf6A479f320ead074411a4B0e7944Ea8c9C1 (100 ETH)
evm2-1 | (1) 0xFFcf8FDEE72ac11b5c542428B35EEF5769C409f0 (100 ETH)
evm2-1 | (2) 0x22d491Bde2303f2f43325b2108D26f1eAbA1e32b (100 ETH)
evm2-1 | (3) 0xE11BA2b4D45Eaed5996Cd0823791E0C93114882d (100 ETH)
evm2-1 | (4) 0xd03ea8624C8C5987235048901fB614fDcA89b117 (100 ETH)
evm2-1 | (5) 0x95cED938F7991cd0dFcb48F0a06a40FA1aF46EBC (100 ETH)
evm2-1 | (6) 0x3E5e9111Ae8eB78Fe1CC3bb8915d5D461F3Ef9A9 (100 ETH)
evm2-1 | (7) 0x28a8746e75304c0780E011BEd21C72cD78cd535E (100 ETH)
evm2-1 | (8) 0xACa94ef8bD5ffEE41947b4585a84BdA5a3d3DA6E (100 ETH)
evm2-1 | (9) 0x1dF62f291b2E969fB0849d99D9Ce41e2F137006e (100 ETH)
evm2-1 |
evm2-1 | Private Keys
evm2-1 | ==================
evm2-1 | (0) 0x4f3edf983ac636a65a842ce7c78d9aa706d3b113bce9c46f30d7d21715b23b1d
evm2-1 | (1) 0x6cbed15c793ce57650b9877cf6fa156fbef513c4e6134f022a85b1ffdd59b2a1
evm2-1 | (2) 0x6370fd033278c143179d81c5526140625662b8daa446c22ee2d73db3707e620c
evm2-1 | (3) 0x646f1ce2fdad0e6deeeb5c7e8e5543bdde65e86029e2fd9fc169899c440a7913
evm2-1 | (4) 0xadd53f9a7e588d003326d1cbf9e4a43c061aadd9bc938c843a79e7b4fd2ad743
evm2-1 | (5) 0x395df67f0c2d2d9fe1ad08d1bc8b6627011959b79c53d7dd6a3536a33ab8a4fd
evm2-1 | (6) 0xe485d098507f54e7733a205420dfddbe58db035fa577fc294ebd14db90767a52
evm2-1 | (7) 0xa453611d9419d0e56f499079478fd72c37b251a94bfde4d19872c44cf65386e3
evm2-1 | (8) 0x829e924fdf021ba3dbbc4225edfece9aca04b929d6e75613329ca6f1d31c0bb4
evm2-1 | (9) 0xb0057716d5917badaf911b193b12b910811c1497b5bada8d7711f758981c3773
evm2-1 |
evm2-1 | HD Wallet
evm2-1 | ==================
evm2-1 | Mnemonic: myth like bonus scare over problem client lizard pioneer submit female collect
evm2-1 | Base HD Path: m/44'/60'/0'/0/{account_index}
evm2-1 |
evm2-1 | Gas Price
evm2-1 | ==================
evm2-1 | 20000000000
evm2-1 |
evm2-1 | Gas Limit
evm2-1 | ==================
evm2-1 | 6721975
evm2-1 |
evm2-1 | Call Gas Limit
evm2-1 | ==================
evm2-1 | 9007199254740991
evm2-1 |
evm2-1 | Listening on 0.0.0.0:8545
example_relayer1 exited with code 0
fee-oracle | time="2022-10-07T15:57:45Z" level=info msg="log level: debug"
fee-oracle | time="2022-10-07T15:57:45Z" level=info msg="fee oracle indentity address: 0x70B7D7448982b15295150575541D1d3b862f7FE9\n" base=base
fee-oracle | time="2022-10-07T15:57:45Z" level=warning msg="remote param operator is disabled" base=base
fee-oracle | time="2022-10-07T15:57:45Z" level=info msg="running in: dev" base=base
fee-oracle | time="2022-10-07T15:57:45Z" level=info msg="running mode: debug" base=base
fee-oracle | time="2022-10-07T15:57:45Z" level=info msg="fee oracle app init success" base=base
fee-oracle | time="2022-10-07T15:57:45Z" level=info msg="http server starts on port :8091 " base=base
fee-oracle | time="2022-10-07T15:57:45Z" level=info msg="http server mode: debug " base=base
fee-oracle | [GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
fee-oracle | - using env: export GIN_MODE=release
fee-oracle | - using code: gin.SetMode(gin.ReleaseMode)
fee-oracle |
fee-oracle | [GIN-debug] GET /health --> github.com/ChainSafe/sygma-fee-oracle/api.(*Handler).healthCheck-fm (3 handlers)
fee-oracle | [GIN-debug] GET /v1/rate/from/:fromDomainID/to/:toDomainID/resourceid/:resourceID --> github.com/ChainSafe/sygma-fee-oracle/api.(*Handler).debugGetRate-fm (3 handlers)
fee-oracle | [GIN-debug] [WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value.
fee-oracle | Please check https://pkg.go.dev/github.com/gin-gonic/gin#readme-don-t-trust-all-proxies for details.
fee-oracle | [GIN-debug] Listening and serving HTTP on :8091
After that, you can watch the logs an see your funds or NFT being transfer from one network to another
This is the list of the current contract addresses deployed in our local setup
Contract | Address |
---|---|
Bridge | 0x6CdE2Cd82a4F8B74693Ff5e194c19CA08c2d1c68 |
ERC20Handler | 0x1ED1d77911944622FCcDDEad8A731fd77E94173e |
ERC721Handler | 0x481f97f9C82a971B3844a422936a4d3c4082bF84 |
GenericHandler | 0x783BB8123b8532CC85C8D2deF2f47C55D1e46b46 |
FeeRouter | 0x9275AC64D6556BE290dd878e5aAA3a5bae08ae0C |
BasicFeeHandler | 0x78E5b9cEC9aEA29071f070C8cC561F692B3511A6 |
ColorsExampleContract | 0xE54Dc792c226AEF99D6086527b98b36a4ADDe56a |
ERC20Token | 0x1CcB4231f2ff299E1E049De76F0a1D2B415C563A |
ERC721Token | 0x8dA96a8C2b2d3e5ae7e668d0C94393aa8D5D3B94 |
If you want to use the bridge admin and import this account to your metamask, use the private key: cc2c32b154490f09f70c1c8d4b997238448d649e0777495863db231c4ced3616
. Address of the bridge admin is: 0x5C1F5961696BaD2e73f73417f07EF55C62a2dC5b
There is a folder with examples ready to be used for the SDK. Currently we have two working (ERC20 transfer and NFT Transfer) with our current local setup. If you decide that is not for you, here is a little guide to get you started with our SDK.
Assuming you are going to use the local setup provider by Sygma, the setup that you need to pass to the Sygma
class is going to have the following structure:
import { Sygma } from "@buildwithsygma/sygma-sdk-core";
const bridgeSetupList: SygmaBridgeSetupList = [
{
domainId: "1",
networkId: 1337,
name: "Local EVM 1",
decimals: 18,
bridgeAddress: "0x6CdE2Cd82a4F8B74693Ff5e194c19CA08c2d1c68",
erc20HandlerAddress: "0x1ED1d77911944622FCcDDEad8A731fd77E94173e",
erc721HandlerAddress: "0x481f97f9C82a971B3844a422936a4d3c4082bF84",
rpcUrl: "http://localhost:8545",
tokens: [
{
type: "erc20",
address: "0x1CcB4231f2ff299E1E049De76F0a1D2B415C563A",
name: "ERC20LRTST",
symbol: "ETHIcon",
imageUri: "ETHIcon",
decimals: 18,
resourceId:
"0x0000000000000000000000000000000000000000000000000000000000000300",
feeSettings: {
type: "basic",
address: "0x78E5b9cEC9aEA29071f070C8cC561F692B3511A6",
},
},
],
},
{
domainId: "2",
networkId: 1338,
name: "Local EVM 2",
decimals: 18,
bridgeAddress: "0x6CdE2Cd82a4F8B74693Ff5e194c19CA08c2d1c68",
erc20HandlerAddress: "0x1ED1d77911944622FCcDDEad8A731fd77E94173e",
erc721HandlerAddress: "0x481f97f9C82a971B3844a422936a4d3c4082bF84",
rpcUrl: "http://localhost:8547",
tokens: [
{
type: "erc20",
address: "0x1CcB4231f2ff299E1E049De76F0a1D2B415C563A",
name: "ERC20LRTST",
symbol: "ETHIcon",
imageUri: "ETHIcon",
decimals: 18,
resourceId:
"0x0000000000000000000000000000000000000000000000000000000000000300",
feeSettings: {
type: "basic",
address: "0x78E5b9cEC9aEA29071f070C8cC561F692B3511A6",
},
},
],
},
];
We are going to use the SDK with NodeJS
so, we are going to use the bridge admin to make the transfer. For this instantiate a wallet"
import { ethers } from 'ethers'
import { NonceManager } from "@ethersproject/experimental";
// connect to one of the nodes
const provider = new ethers.providers.JsonRpcProvider('http://localhost:8545');
// instantiate the waller with the following mnemonic
const wallet = ethers.Wallet.fromMnemonic(
"black toward wish jar twin produce remember fluid always confirm bacon slush",
"m/44'/60'/0'/0/0",
);
// connect the wallet to the provider
const walletConnected = wallet.connect(provider);
// use the NonceManager to avoid issues with the nonce
const managedSigner = new NonceManager(walletConnected);
Then we create a setup
object to pass to the Sygma
class. This setup uses the bridgeSetup
defined above with the contract addresses.
const setup = { bridgeSetup }
const chainbridge = new Sygma(setup)
Now we are ready to initialize connection:
const bridgeEvents = sygma.initializeConnectionRPC(wallet.address)
With this we can get the basic Fee rate to use in our first deposit:
const basicFeeRate = await sygma.fetchFeeData({
amount: "1",
recipientAddress: wallet.address
})
Once this is complete, we can approve the amount of tokens to transfer before we make the deposit:
const approvalTxReceipt = await (await sygma.approve({
amountForApproval: "1",
})).wait(1)
const depositTx = await sygma.deposit({
amount: "1",
recipientAddress: wallet.address
feeData: basicFee
})
const depositEVent = await sygma.getDepositEventFromReceipt(depositTx)
const { depositNonce } = depositEvent.args
console.log("Result of transfer", depositTx)
console.log("Nonce of tranfer" , depositNonce)
For usage in the browser with our local setup, provide the same bridge config that you use for the NodeJS context:
import { Sygma } from "@buildwithsygma/sygma-sdk-core";
type LocalData = {
balance: BigNumber;
address: string;
gasPrice: BigNumber;
balanceOfTokens: BigNumber;
tokenName: string;
};
const bridgeSetupList: SygmaBridgeSetupList = [
{
domainId: "1",
networkId: 1337,
name: "Local EVM 1",
decimals: 18,
bridgeAddress: "0x6CdE2Cd82a4F8B74693Ff5e194c19CA08c2d1c68",
erc20HandlerAddress: "0x1ED1d77911944622FCcDDEad8A731fd77E94173e",
erc721HandlerAddress: "0x481f97f9C82a971B3844a422936a4d3c4082bF84",
rpcUrl: "http://localhost:8545",
tokens: [
{
type: "erc20",
address: "0x1CcB4231f2ff299E1E049De76F0a1D2B415C563A",
name: "ERC20LRTST",
symbol: "ETHIcon",
imageUri: "ETHIcon",
decimals: 18,
resourceId:
"0x0000000000000000000000000000000000000000000000000000000000000300",
feeSettings: {
type: "basic",
address: "0x78E5b9cEC9aEA29071f070C8cC561F692B3511A6",
},
},
],
},
{
domainId: "2",
networkId: 1338,
name: "Local EVM 2",
decimals: 18,
bridgeAddress: "0x6CdE2Cd82a4F8B74693Ff5e194c19CA08c2d1c68",
erc20HandlerAddress: "0x1ED1d77911944622FCcDDEad8A731fd77E94173e",
erc721HandlerAddress: "0x481f97f9C82a971B3844a422936a4d3c4082bF84",
rpcUrl: "http://localhost:8547",
tokens: [
{
type: "erc20",
address: "0x1CcB4231f2ff299E1E049De76F0a1D2B415C563A",
name: "ERC20LRTST",
symbol: "ETHIcon",
imageUri: "ETHIcon",
decimals: 18,
resourceId:
"0x0000000000000000000000000000000000000000000000000000000000000300",
feeSettings: {
type: "basic",
address: "0x78E5b9cEC9aEA29071f070C8cC561F692B3511A6",
},
},
],
},
];
Then, inside your App, create some state variables and functions to get account data from your wallet (in this example the wallet is Metamask
)
function App(){
const [bridge, setBridge] = useState<any | undefined>(undefined)
const [logicConnected, setLogicConnected] = useState<boolean>(false);
const [sygmaInstance, setSygmaInstance] = useState<Sygma | undefined>(undefined);
const getAccountData = async (sygma: Sygma) => {
try {
const balance =
(await sygma.getSignerBalance("chain1")) ?? BigNumber.from("0");
const address = await sygma.getSignerAddress("chain1");
const gasPrice = await sygma.getSignerGasPrice("chain1");
const { balanceOfTokens, tokenName } = await sygma.getTokenInfo(
"chain1"
);
console.log("signer balance", utils.formatEther(balance!));
console.log("signer address", address);
console.log("gas price", utils.formatEther(gasPrice!));
console.log("balance of tokens", utils.formatUnits(balanceOfTokens, 18));
setValue("address", address!)
setAccountData({
balance: balance!,
address: address!,
gasPrice: gasPrice!,
balanceOfTokens: balanceOfTokens!,
tokenName: tokenName!,
});
setIsReady(true);
} catch (e) {
console.log(e);
console.log("Perhaps you forget to deploy the bridge?");
}
};
useEffect(() => {
if (data !== undefined && sygmaInstance !== undefined) {
getAccountData(sygmaInstance);
}
}, [data, logicConnected]);
useEffect(() => {
if (metaIsConnected && sygmaInstance !== undefined) {
handleConnect();
getAccountData(sygmaInstance! as Sygma);
// Update form values using useForm
setValue("from", sygmaInstance.bridgeSetup?.chain1.domainId!)
setValue("to", sygmaInstance.bridgeSetup?.chain2.domainId!)
}
}, [metaIsConnected]);
}
If you are using Metamask
you can create a function to trigger the connection to the extension and at the same time instantiate Sygma
SDK:
// in the App component, below the last useEffect
const handleConnect = () => {
// IF META IS NOT SIGNIN, TRIGGER POP OF THE WINDOW FOR THE EXTENSION
if (!metaIsConnected) {
return window.ethereum
.request({ method: "eth_requestAccounts" })
.then((r: any) => {
console.log("request to unlock metamask", r);
const [addr] = r;
setMetaIsConnected(true);
setAccountData({
...(accountData as LocalData),
address: addr,
});
})
.catch((error: any) => {
if (error.code === 4001) {
// EIP-1193 userRejectedRequest error
console.log("Please connect to MetaMask.");
} else {
console.error(error);
}
});
} else if (metaIsConnected) {
const data = (sygmaInstance as Sygma).initializeConnectionFromWeb3Provider(
window.ethereum
);
//@ts-ignore-line
setData(data);
setLogicConnected(true);
}
};
To listen to deposit events on the home network:
// initialization of Sygma class
// ...
sygmaInstance.createHomeChainDepositEventListener((
destinationDomainId: any,
resourceId: any,
depositNonce: any,
user: any,
data: any,
handleResponse: any,
tx: any
) => {
console.log(
`bride deposit event deposit nonce: ${depositNonce.toString()} to contract with ResourceId: ${resourceId}`
);
console.log(` transaction hash: ${tx.transactionHash}`);
console.info("Deposit in transit!");
});
To remove deposit events listener:
// initialization of Sygma class
// sygmaInstance.createHomeChainDepositEventListener(...)
// ...
sygmaInstance.removeHomeChainDepositEventListener()
To listen for execution events on the destination network:
// initialization of Sygma class
// ...
sygmaInstance.destinationProposalExecutionEventListener((
originDomainId: any,
despositNonce: any,
dataHash: any,
tx: any
) => {
console.warn("Proposal execution event!")
console.log({originDomainId, despositNonce, dataHash, tx} )
console.warn("Transfer complete!")
});
To remove remove execution events listener:
// initialization of Sygma class
// sygmaInstance.createHomeChainDepositEventListener(...)
// ...
sygmaInstance.removeDestinationProposalExecutionEventListener()
With this you can use our SDK and create the render logic to show your tokens and your networks of the bridge. For a more in depth review, check out our erc20
, erc721
and generic handler
examples.
GNU Lesser General Public License v3.0