UNPKG

@d8x/perpetuals-sdk

Version:

Node TypeScript SDK for D8X Perpetual Futures

248 lines 10.6 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const ethers_1 = require("ethers"); const nodeSDKTypes_1 = require("./nodeSDKTypes"); /** * This is a 'standalone' class that deals with signatures * required for referral codes: * - referrer creates a new referral code for trader (no agency involved) * - agency creates a new referral code for a referrer and their trader * - trader selects a referral code to trade with * * Note that since the back-end is chain specific, the referral code is typically bound to * one chain, unless the backend employs code transferrals */ class ReferralCodeSigner { constructor(signer, address, _rpcURL) { this.rpcURL = _rpcURL; this.address = address; if (typeof signer == "string") { const wallet = this.createSignerInstance(signer); this.signingFun = (x) => wallet.signMessage(x); this.signingTypedDataFun = (domain, types, value) => wallet.signTypedData(domain, types, value); } else if ("signMessage" in signer) { this.signingFun = (x) => signer.signMessage(x); this.signingTypedDataFun = (domain, types, value) => signer.signTypedData(domain, types, value); } else if (signer.length === 1) { this.signingFun = signer; } else { this.signingTypedDataFun = signer; } } createSignerInstance(_privateKey) { this.provider = new ethers_1.JsonRpcProvider(this.rpcURL); const wallet = new ethers_1.Wallet(_privateKey); wallet.connect(this.provider); this.signingFun = (x) => wallet.signMessage(x); this.signingTypedDataFun = (domain, types, value) => wallet.signTypedData(domain, types, value); return wallet; } async getSignatureForNewReferral(rp) { if (this.signingTypedDataFun != undefined) { return await this.signingTypedDataFun(nodeSDKTypes_1.referralDomain, { NewReferral: [...nodeSDKTypes_1.referralTypes.NewReferral], }, ReferralCodeSigner.newReferralPayloadToTypedData(rp)); } else if (this.signingFun != undefined) { return await ReferralCodeSigner.getSignatureForNewReferral(rp, this.signingFun); } else { throw Error("no signer defined, call createSignerInstance()"); } } async getSignatureForNewCode(rc) { if (this.signingTypedDataFun != undefined) { return await this.signingTypedDataFun(nodeSDKTypes_1.referralDomain, { NewCode: [...nodeSDKTypes_1.referralTypes.NewCode], }, ReferralCodeSigner.referralCodeNewCodePayloadToTypedData(rc)); } else if (this.signingFun != undefined) { return await ReferralCodeSigner.getSignatureForNewCode(rc, this.signingFun); } else { throw Error("no signer defined, call createSignerInstance()"); } } async getSignatureForCodeSelection(rc) { if (this.signingTypedDataFun != undefined) { return await this.signingTypedDataFun(nodeSDKTypes_1.referralDomain, { CodeSelection: [...nodeSDKTypes_1.referralTypes.CodeSelection], }, ReferralCodeSigner.codeSelectionPayloadToTypedData(rc)); } else if (this.signingFun != undefined) { return await ReferralCodeSigner.getSignatureForCodeSelection(rc, this.signingFun); } else { throw Error("no signer defined, call createSignerInstance()"); } } async getAddress() { if (this.signingFun == undefined) { throw Error("no signer defined, call createSignerInstance()"); } return this.address; } /** * New agency/broker to agency referral * rc.PassOnPercTDF must be in 100*percentage unit * @param rc payload to sign * @param signingFun signing function * @returns signature */ static async getSignatureForNewReferral(rp, signingFun) { if (Math.abs(rp.passOnPercTDF - Math.round(rp.passOnPercTDF)) > 1e-4) { throw Error("PassOnPercTDF must be in 100*percentage unit, e.g., 2.25% -> 225"); } let digest = ReferralCodeSigner._referralNewToMessage(rp); let digestBuffer = Buffer.from(digest.substring(2, digest.length), "hex"); return await signingFun(digestBuffer); } /** * New code * rc.PassOnPercTDF must be in 100*percentage unit * @param rc APIReferralCodePayload without signature * @param signingFun function that signs * @returns signature string */ static async getSignatureForNewCode(rc, signingFun) { if (Math.abs(rc.passOnPercTDF - Math.round(rc.passOnPercTDF)) > 1e-4) { throw Error("PassOnPercTDF must be in 100*percentage unit, e.g., 2.25% -> 225"); } let digest = ReferralCodeSigner._referralCodeNewCodePayloadToMessage(rc); let digestBuffer = Buffer.from(digest.substring(2, digest.length), "hex"); return await signingFun(digestBuffer); } static async getSignatureForCodeSelection(rc, signingFun) { let digest = ReferralCodeSigner._codeSelectionPayloadToMessage(rc); let digestBuffer = Buffer.from(digest.substring(2, digest.length), "hex"); return await signingFun(digestBuffer); } static _referralNewToMessage(rc) { let abiCoder = new ethers_1.AbiCoder(); const passOnPercTwoDigitsFormat = Math.round(rc.passOnPercTDF); let digest = (0, ethers_1.keccak256)(abiCoder.encode(["address", "address", "uint32", "uint256"], [rc.parentAddr, rc.referToAddr, passOnPercTwoDigitsFormat, Math.round(rc.createdOn)])); return digest; } /** * Convert payload to data struct to sign * @param rc payload * @returns typed data */ static newReferralPayloadToTypedData(rc) { return { ParentAddr: rc.parentAddr, ReferToAddr: rc.referToAddr, PassOnPercTDF: Math.round(rc.passOnPercTDF), CreatedOn: BigInt(Math.round(rc.createdOn)), }; } /** * Create digest for referralCodePayload that is to be signed * @param rc payload * @returns the hex-string to be signed */ static _referralCodeNewCodePayloadToMessage(rc) { let abiCoder = new ethers_1.AbiCoder(); const passOnPercTwoDigitsFormat = Math.round(rc.passOnPercTDF); let digest = (0, ethers_1.keccak256)(abiCoder.encode(["string", "address", "uint32", "uint256"], [rc.code, rc.referrerAddr, passOnPercTwoDigitsFormat, Math.round(rc.createdOn)])); return digest; } /** * Convert payload to data struct to sign * @param rc payload * @returns typed data */ static referralCodeNewCodePayloadToTypedData(rc) { return { Code: rc.code, ReferrerAddr: rc.referrerAddr, PassOnPercTDF: Math.round(rc.passOnPercTDF), CreatedOn: BigInt(Math.round(rc.createdOn)), }; } /** * Create digest for APIReferralCodeSelectionPayload that is to be signed * @param rc payload * @returns the hex-string to be signed */ static _codeSelectionPayloadToMessage(rc) { let abiCoder = new ethers_1.AbiCoder(); let digest = (0, ethers_1.keccak256)(abiCoder.encode(["string", "address", "uint256"], [rc.code, rc.traderAddr, Math.round(rc.createdOn)])); return digest; } /** * Convert payload to data struct to sign * @param rc payload * @returns typed data */ static codeSelectionPayloadToTypedData(rc) { return { Code: rc.code, TraderAddr: rc.traderAddr, CreatedOn: BigInt(Math.round(rc.createdOn)), }; } /** * Check whether signature is correct on payload: * - the referrer always signs * - if the agency is not an agency for this referrer, the backend will reject * @param rc referralcode payload with a signature * @returns true if correctly signed, false otherwise */ static checkNewCodeSignature(rc) { if (rc.signature == undefined || rc.signature == "") { return false; } try { // typed-data (^2.x.x) const typedData = ReferralCodeSigner.referralCodeNewCodePayloadToTypedData(rc); const signerAddress = (0, ethers_1.verifyTypedData)(nodeSDKTypes_1.referralDomain, { NewCode: [...nodeSDKTypes_1.referralTypes.NewCode] }, typedData, rc.signature); if (rc.referrerAddr.toLowerCase() == signerAddress.toLowerCase()) { return true; } } catch (err) { console.log("invalid eip-712 signature:", err); } // digest (1.x.x) try { let digest = ReferralCodeSigner._referralCodeNewCodePayloadToMessage(rc); let digestBuffer = Buffer.from(digest.substring(2, digest.length), "hex"); const signerAddress = (0, ethers_1.verifyMessage)(digestBuffer, rc.signature); return rc.referrerAddr.toLowerCase() == signerAddress.toLowerCase(); } catch (err) { console.log("invalid eip-191 signature:", err); } return false; } static checkCodeSelectionSignature(rc) { if (rc.signature == undefined || rc.signature == "") { return false; } try { // typed-data (^2.x.x) const typedData = ReferralCodeSigner.codeSelectionPayloadToTypedData(rc); const signerAddress = (0, ethers_1.verifyTypedData)(nodeSDKTypes_1.referralDomain, { CodeSelection: [...nodeSDKTypes_1.referralTypes.CodeSelection] }, typedData, rc.signature); return rc.traderAddr.toLowerCase() == signerAddress.toLowerCase(); } catch (err) { console.log(err); // digest (1.x.x) try { let digest = ReferralCodeSigner._codeSelectionPayloadToMessage(rc); let digestBuffer = Buffer.from(digest.substring(2, digest.length), "hex"); const signerAddress = (0, ethers_1.verifyMessage)(digestBuffer, rc.signature); return rc.traderAddr.toLowerCase() == signerAddress.toLowerCase(); } catch (err) { return false; } } } } exports.default = ReferralCodeSigner; //# sourceMappingURL=referralCodeSigner.js.map