Signature
Definition: Loopring SDK support EOA (EOA hardware wallet) & Loopring smart wallet signature.
Environment type
For Browser extension, Dapp, and hardware wallet, we only support EOA
For Loopring smart wallet (App), the provider gateway is WalletConnect
The following are the provider gateways we inject & test:
MetaMask (Ledger, Trezor)
WalletConnect (Authereum, Loopring smart wallet )
Coinbase
For signature:
Understand what is
eth_sign signing types
(eth_sign, personal_sign, v1, v3, v4)❗ when add
SigSuffix
02|03
(please follow:EIP712
&signTypedData_v4
+02
,personal_sign
+03
)
for
v4
ecdsaSignature the result signature should +SigSuffix.Suffix02
;for
personal_sign
ecdsaSignature the result signature should +SigSuffix.Suffix03
;
EOA:
For Browser extension (More information: signing-data)
common EOA we use the
v4
signature andweb3.eth.personal.ecRecover
validate signaturewhen
v4
signature is failed for any step, we will trypersonal_sign
andweb3.eth.personal.ecRecover
validate signature
For Dapp
when loopring Dex is inside Dapp Webview & connect by
window.ethereum
, we remove theweb3.eth.personal.ecRecover
validate
Loopring smart wallet:
For Loopring smart wallet we send
eth_signTypedData
by walletConnect & validate ABI.Contracts.ContractWallet.encodeInputsisValidSignature(bytes32,bytes)
Loopring counterfactual wallet:
signature is same as Loopring smart wallet
But ecRecover is by walletOwner,
const {walletOwner} = await LoopringAPI.exchangeAPI.getCounterFactualInfo({ accountId: LOOPRING_EXPORTED_ACCOUNT.accountIdCF, });
Demo Signature
generateKeyPair
const {accInfo} = await LoopringAPI.exchangeAPI.getAccount({
owner: LOOPRING_EXPORTED_ACCOUNT.address,
});
const result = await signatureKeyPairMock(accInfo);
console.log(result.sk);
getEcDSASig: eth_signTypedData_v4
// test case is not allow brock by Mock provider
const result = await sdk.getEcDSASig(
web3,
testTypedData,
LOOPRING_EXPORTED_ACCOUNT.address,
sdk.GetEcDSASigType.HasDataStruct,
sdk.ChainId.GOERLI,
LOOPRING_EXPORTED_ACCOUNT.accountId,
"",
sdk.ConnectorNames.Unknown
);
console.log("getEcDSASig:eth_signTypedData_v4",
result,
"ecdsaSig+sdk.SigSuffix.Suffix02",
result.ecdsaSig + sdk.SigSuffix.Suffix02
);
getEcDSASig: personalSign(WithoutDataStruct--Hardware wallet)
const result = await sdk.getEcDSASig(
web3,
testTypedData,
LOOPRING_EXPORTED_ACCOUNT.address,
sdk.GetEcDSASigType.WithoutDataStruct,
sdk.ChainId.GOERLI,
LOOPRING_EXPORTED_ACCOUNT.accountId,
"",
sdk.ConnectorNames.Unknown
);
console.log(
"getEcDSASig:WithoutDataStruct(personalSign)",
result,
"ecdsaSig+sdk.SigSuffix.Suffix03",
result.ecdsaSig + sdk.SigSuffix.Suffix03
);
getEcDSASig: personalSign(Contract)
// test case is not allow brock by Mock provider
const result = await sdk.getEcDSASig(
web3,
testTypedData,
LOOPRING_EXPORTED_ACCOUNT.address,
sdk.GetEcDSASigType.Contract,
sdk.ChainId.GOERLI,
LOOPRING_EXPORTED_ACCOUNT.accountId,
"",
sdk.ConnectorNames.Unknown
);
console.log(
"getEcDSASig:personalSign(Contract)",
result
);
Validate signature
github: src/api/base_api.ts#personalSign
export async function personalSign(
web3: any,
account: string | undefined,
pwd: string,
msg: string,
walletType: ConnectorNames,
chainId: ChainId,
accountId?: number,
counterFactualInfo?: CounterFactualInfo,
isMobile?: boolean
) {
if (!account) {
return { error: "personalSign got no account" };
}
return new Promise((resolve) => {
try {
web3.eth.personal.sign(
msg,
account,
pwd,
async function (err: any, result: any) {
if (!err) {
// Valid:1. counter Factual signature Valid
if (counterFactualInfo && accountId) {
myLog("fcWalletValid counterFactualInfo accountId:");
const fcValid = await fcWalletValid(
web3,
account,
msg,
result,
accountId,
chainId,
counterFactualInfo
);
if (fcValid.result) {
resolve({
sig: result,
counterFactualInfo: fcValid.counterFactualInfo,
});
return;
}
}
// Valid: 2. webview directory signature Valid
if (
(window?.ethereum?.isImToken || window?.ethereum?.isMetaMask) &&
isMobile &&
// Mobile directory connect will sign ConnectorNames as MetaMask only
walletType === ConnectorNames.MetaMask
) {
const address: string[] = await window.ethereum?.request({
method: "eth_requestAccounts",
});
if (
address?.find(
(item) => item.toLowerCase() === account.toLowerCase()
)
) {
return resolve({ sig: result });
}
}
// Valid: 3. EOA signature Valid by ecRecover
const valid: any = await ecRecover(web3, account, msg, result);
if (valid.result) {
return resolve({ sig: result });
}
// Valid: 4. contractWallet signature Valid `isValidSignature(bytes32,bytes)`
const walletValid2: any = await contractWalletValidate32(
web3,
account,
msg,
result
);
if (walletValid2.result) {
return resolve({ sig: result });
}
// Valid: 5. counter Factual signature Valid when no counterFactualInfo
if (accountId) {
const fcValid = await fcWalletValid(
web3,
account,
msg,
result,
accountId,
chainId
);
if (fcValid.result) {
return resolve({
sig: result,
counterFactualInfo: fcValid.counterFactualInfo,
});
}
}
// Valid: 6. myKeyValid Valid again
const myKeyValid: any = await mykeyWalletValid(
web3,
account,
msg,
result
);
if (myKeyValid.result) {
return resolve({ sig: result });
}
// Valid: Error cannot pass personalSign Valid
// eslint-disable-next-line no-console
console.log(
"web3.eth.personal.sign Valid, valid 5 ways, all failed!"
);
return resolve({
error: "web3.eth.personal.sign Valid, valid 5 ways, all failed!",
});
} else {
return resolve({
error: "personalSign err before Validate:" + err,
});
}
}
);
} catch (reason) {
resolve({ error: reason });
}
});
}
Last updated
Was this helpful?