Skip to content

Instantly share code, notes, and snippets.

@bsx-engineering
Last active September 23, 2024 08:09
Show Gist options
  • Save bsx-engineering/c61390a3b4bc44e5e73e82f561078596 to your computer and use it in GitHub Desktop.
Save bsx-engineering/c61390a3b4bc44e5e73e82f561078596 to your computer and use it in GitHub Desktop.
Signing order script

Signing Order Script

Javascript

Put package.json and createSignature.js in the sample folder and run

yarn
node createSignature.js
const { parseEther, Wallet } = require("ethers");
// the domain is for the same for all messages; you can hardcode it or derive it
// from this api https://api.testnet.bsx.exchange/chain/configs
const domainData = {
name: "BSX Testnet",
version: "1",
chainId: 84532,
verifyingContract: "0x182C14399BCC7c9F0280Ad07913CB07ba1775Cb6",
};
// replace with your account private key
const account = new Wallet(
"0x22f2f8bd71ba7dc47e762cc70b7e5db5ecc45ea4a7babc5268782c58c79c346e"
);
console.log("account", account.address);
//account 0x5A3Ac91883Fd94394F3CFf22dBC93AFB41d5eD3f
const signer = new Wallet(
"0xdd86166bd13bbfa046967a9b8aa6e2e3c10a8b0bcef902fe63b01ba873148086"
);
console.log("signer", signer.address);
//signer 0x2e086F40822314CFDb72DBeD9C6FCbaEA0Db1c6d
// this function show example how to build the BE for POST /users/register
const registerSigningKey = async () => {
const signingKeyMessage = {
account: account.address,
};
const signingKeySignature = await signer.signTypedData(
domainData,
{
SignKey: [{ name: "account", type: "address" }],
},
signingKeyMessage
);
console.log("signingKeySignature", signingKeySignature);
//signingKeySignature: 0x5c49b8bcd90c0848696756b132ceb79ecdc3f572ae24cc5bcbd9b299437b24235fcd525dac0545e17e4f65fd51717fa767762a474ca4a82520c6b0b946687b5d1b
const registerMessage = {
key: signer.address,
message:
"Please sign in with your wallet to access bsx.exchange. You are signing in on Monday, January 17, 2124 9:35:29 AM (GMT). This message is exclusively signed with bsx.exchange for security.",
nonce: "4861157729000000000",
};
const accountSignature = await account.signTypedData(
domainData,
{
Register: [
{ name: "key", type: "address" },
{ name: "message", type: "string" },
{ name: "nonce", type: "uint64" },
],
},
registerMessage
);
console.log("accountSignature", accountSignature);
//registerMessage expected: 0xa19f4468cdf8c774ea2a86488a5747aa8ec91c13bd00c839d8725b9eaf594253792c38f54a17176f83312867e78c9d69067be8d922e3f1ddc9efdcfb3cd1d1051b
const body = {
user_wallet: account.address,
signer: signer.address,
nonce: registerMessage.nonce,
wallet_signature: accountSignature,
signer_signature: signingKeySignature,
message: registerMessage.message,
};
console.log("SIGNING_BODY:::", body);
// SIGNING_BODY::: {
// user_wallet: '0x5A3Ac91883Fd94394F3CFf22dBC93AFB41d5eD3f',
// signer: '0x2e086F40822314CFDb72DBeD9C6FCbaEA0Db1c6d',
// nonce: '4861157729000000000',
// wallet_signature: '0x22ae531bd56c27930eb16b407b43f2a5e5b8aa57d5b8d13a8c0763dea2ae03037876449da50f1d475c5d9c73ce71fb591857d17647171fa87033a189bd2db85a1c',
// signer_signature: '0x833896aee9c6b0a6c4985fb983a9659c7eb4f23d0824aaddc3d23bb6148ac90c6d705f928da1b6a1e61523325cecfcd82ff647c3c45f6054f61ac7f6326e9a3f1b',
// message: 'Please sign in with your wallet to access bsx.exchange. You are signing in on Monday, January 17, 2124 9:35:29 AM (GMT). This message is exclusively signed with bsx.exchange for security.'
// }
};
// this functions show example how to build the BE for POST /orders
const onCreateOrder = async (payload) => {
const {
side,
product_index,
type = "LIMIT",
price,
size,
time_in_force = type === "MARKET" ? "FOK" : "GTC",
nonce,
} = payload;
// to sign the order we need to build the message
// 1 thing should be noted here is that the order message is a little bit different from the other messages
// the price, size in body is float, we must convert it to uint128 (by using parseEther - it simply multiply the float by 10^18, so we have an integer)
const orderMessage = {
sender: account.address,
size: parseEther(size).toString(), // this field is different in the request body
price: parseEther(price).toString(), // this field is different in the request body
nonce: nonce,
productIndex: product_index,
orderSide: side === "BUY" ? 0 : 1,
};
const orderSignature = await signer.signTypedData(
domainData,
{
Order: [
{ name: "sender", type: "address" },
{ name: "size", type: "uint128" },
{ name: "price", type: "uint128" },
{ name: "nonce", type: "uint64" },
{ name: "productIndex", type: "uint8" },
{ name: "orderSide", type: "uint8" },
],
},
orderMessage
);
console.log("orderSignature", orderSignature);
//orderSignature expected: 0xafd6693decaeb17d005b788171ca942b74cd407f12dcccb0e87f1201e48ba9050bec18054301a00e6769e9e6cf50d24b3c287fb7fed59b2b41129b91edeffc4c1b
const body = {
side: side,
product_index: product_index,
price: price,
size: size,
time_in_force: time_in_force,
nonce: nonce,
signature: orderSignature,
};
console.log("ORDER_BODY:::", body);
// ORDER_BODY::: {
// side: 'BUY',
// product_index: 1,
// price: '2500',
// size: '2',
// time_in_force: 'GTC',
// nonce: '1234123412341234',
// signature: '0x2aa5b3fa914a5aae71cc0d7799f3ec469add02f1459e5c588fa2c9e05a5e6b5a7ab59f790d76b873d7904bc43e0e24dcccca671c4053c775d1e6395cb99e264a1b'
// }
};
const onCreateWithdraw = async (payload) => {
const withdrawMessage = {
sender: account.address,
token: payload.token,
amount: parseEther(payload.amount), // payload.amount is float, we must convert it to uint128 (by using parseEther)
nonce: payload.nonce,
};
const withdrawSignature = await signer.signTypedData(
domainData,
{
Withdraw: [
{ name: "sender", type: "address" },
{ name: "token", type: "address" },
{ name: "amount", type: "uint128" },
{ name: "nonce", type: "uint64" },
],
},
withdrawMessage
);
console.log("withdrawSignature", withdrawSignature);
//withdrawSignature expected: 0x01479ab04e421ecb07ad18556a7edf1a90e85ead460056aca28bf30d22a1af074ba633567ee2b2f3ed4effae21e78ad7217b53595138c456b3959ff4139507731b
const body = {
sender: account.address,
token: payload.token,
amount: payload.amount,
nonce: payload.nonce,
wallet_signature: withdrawSignature,
};
console.log("WITHDRAW_BODY:::", body);
// WITHDRAW_BODY::: {
// sender: '0x5A3Ac91883Fd94394F3CFf22dBC93AFB41d5eD3f',
// token: '0x26dF8d79C4FaCa88d0212f0bd7C4A4d1e8955F0e',
// amount: '1.2',
// nonce: '1230004310000',
// wallet_signature: '0x557e80b720b7f3a14494182ab1ae096ab9fd32e8b85444022a132ff0fe4f580f373fc785370b681065d4fed95059ce10a3e67e5bdeae4d33c016624c0d897b541b'
// }
};
(async () => {
await registerSigningKey();
await onCreateOrder({
side: "BUY",
product_index: 1,
price: "2500",
size: "2",
signing_time: "1690434000000000000",
nonce: "1234123412341234",
});
await onCreateWithdraw({
token: "0x26dF8d79C4FaCa88d0212f0bd7C4A4d1e8955F0e",
amount: "1.2",
nonce: "1230004310000",
});
})();
{
"name": "singing-script",
"version": "1.0.0",
"dependencies": {
"dayjs": "^1.11.9",
"ethers": "^6.9.2"
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment