Skip to content

Commit

Permalink
Mock Adapter with Deterministic Private Key (#130)
Browse files Browse the repository at this point in the history
  • Loading branch information
bh2smith authored Oct 10, 2024
1 parent a8fc45e commit 0c11b97
Show file tree
Hide file tree
Showing 9 changed files with 199 additions and 22 deletions.
1 change: 1 addition & 0 deletions .github/workflows/e2e.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,4 @@ jobs:
MPC_CONTRACT_ID: v1.signer-prod.testnet
NEAR_ACCOUNT_ID: ${{secrets.NEAR_ACCOUNT_ID}}
NEAR_ACCOUNT_PRIVATE_KEY: ${{secrets.NEAR_PK}}
ETH_PK: ${{secrets.ETH_PK}}
21 changes: 18 additions & 3 deletions src/chains/ethereum.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,26 +16,26 @@ import {
FunctionCallTransaction,
TxPayload,
SignArgs,
MpcContract,
Network,
buildTxPayload,
populateTx,
toPayload,
broadcastSignedTransaction,
SignRequestData,
NearEthTxData,
IMpcContract,
} from "..";
import { Beta } from "../beta";
import { requestRouter } from "../utils/request";

export class NearEthAdapter {
readonly mpcContract: MpcContract;
readonly mpcContract: IMpcContract;
readonly address: Address;
readonly derivationPath: string;
readonly beta: Beta;

private constructor(config: {
mpcContract: MpcContract;
mpcContract: IMpcContract;
derivationPath: string;
sender: Address;
}) {
Expand Down Expand Up @@ -78,6 +78,21 @@ export class NearEthAdapter {
});
}

/**
* Constructs an EVM instance with the provided configuration.
* @param {AdapterParams} args - The configuration object for the Adapter instance.
*/
static async mocked(args: AdapterParams): Promise<NearEthAdapter> {
// Sender is uniquely determined by the derivation path!
const mpcContract = args.mpcContract;
const derivationPath = args.derivationPath || "ethereum,1";
return new NearEthAdapter({
sender: await mpcContract.deriveEthAddress(derivationPath),
derivationPath,
mpcContract,
});
}

/**
* Takes a minimally declared Ethereum Transaction,
* builds the full transaction payload (with gas estimates, prices etc...),
Expand Down
6 changes: 4 additions & 2 deletions src/constants.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/// A short list of networks with known wrapped tokens.
const ETHER_ICON = "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNjAiIGhlaWdodD0iNjAiIHZpZXdCb3g9IjYgNiA0OCA0OCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4NCjxnIGZpbHRlcj0idXJsKCNmaWx0ZXIwX2RfOTExNV80MDk4NSkiPg0KPGNpcmNsZSBjeD0iMzAiIGN5PSIzMCIgcj0iMjQiIGZpbGw9IiM2NzgwRTMiLz4NCjwvZz4NCjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMF85MTE1XzQwOTg1KSI+DQo8cGF0aCBkPSJNMzAuNDk4MiAxNUwzMC4zMTI1IDE1LjY2MDlWMzQuODM2M0wzMC40OTgyIDM1LjAzMDRMMzguOTk1NSAyOS43NjlMMzAuNDk4MiAxNVoiIGZpbGw9IiNDMkNCRjMiLz4NCjxwYXRoIGQ9Ik0zMC40OTc1IDE1TDIyIDI5Ljc2OUwzMC40OTc1IDM1LjAzMDRWMjUuNzIzMlYxNVoiIGZpbGw9IndoaXRlIi8+DQo8cGF0aCBkPSJNMzAuNDk5MiAzNi43MTU3TDMwLjM5NDUgMzYuODQ5NFY0My42Nzk5TDMwLjQ5OTIgNDQuMDAwMUwzOS4wMDE3IDMxLjQ1N0wzMC40OTkyIDM2LjcxNTdaIiBmaWxsPSIjQzBDQUYyIi8+DQo8cGF0aCBkPSJNMzAuNDk3NSA0NC4wMDAxVjM2LjcxNTdMMjIgMzEuNDU3TDMwLjQ5NzUgNDQuMDAwMVoiIGZpbGw9IndoaXRlIi8+DQo8cGF0aCBkPSJNMzAuNSAzNS4wMzAzTDM4Ljk5NzMgMjkuNzY5TDMwLjUgMjUuNzIzMVYzNS4wMzAzWiIgZmlsbD0iIzg1OThFOCIvPg0KPHBhdGggZD0iTTIyIDI5Ljc2OUwzMC40OTc1IDM1LjAzMDNWMjUuNzIzMUwyMiAyOS43NjlaIiBmaWxsPSIjQzJDQkYzIi8+DQo8L2c+DQo8ZGVmcz4NCjxmaWx0ZXIgaWQ9ImZpbHRlcjBfZF85MTE1XzQwOTg1IiB4PSIwIiB5PSIwIiB3aWR0aD0iNjAiIGhlaWdodD0iNjAiIGZpbHRlclVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgY29sb3ItaW50ZXJwb2xhdGlvbi1maWx0ZXJzPSJzUkdCIj4NCjxmZUZsb29kIGZsb29kLW9wYWNpdHk9IjAiIHJlc3VsdD0iQmFja2dyb3VuZEltYWdlRml4Ii8+DQo8ZmVDb2xvck1hdHJpeCBpbj0iU291cmNlQWxwaGEiIHR5cGU9Im1hdHJpeCIgdmFsdWVzPSIwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAxMjcgMCIgcmVzdWx0PSJoYXJkQWxwaGEiLz4NCjxmZU9mZnNldC8+DQo8ZmVHYXVzc2lhbkJsdXIgc3RkRGV2aWF0aW9uPSIzIi8+DQo8ZmVDb21wb3NpdGUgaW4yPSJoYXJkQWxwaGEiIG9wZXJhdG9yPSJvdXQiLz4NCjxmZUNvbG9yTWF0cml4IHR5cGU9Im1hdHJpeCIgdmFsdWVzPSIwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwLjA3IDAiLz4NCjxmZUJsZW5kIG1vZGU9Im5vcm1hbCIgaW4yPSJCYWNrZ3JvdW5kSW1hZ2VGaXgiIHJlc3VsdD0iZWZmZWN0MV9kcm9wU2hhZG93XzkxMTVfNDA5ODUiLz4NCjxmZUJsZW5kIG1vZGU9Im5vcm1hbCIgaW49IlNvdXJjZUdyYXBoaWMiIGluMj0iZWZmZWN0MV9kcm9wU2hhZG93XzkxMTVfNDA5ODUiIHJlc3VsdD0ic2hhcGUiLz4NCjwvZmlsdGVyPg0KPGNsaXBQYXRoIGlkPSJjbGlwMF85MTE1XzQwOTg1Ij4NCjxyZWN0IHdpZHRoPSIxNyIgaGVpZ2h0PSIyOSIgZmlsbD0id2hpdGUiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDIyIDE1KSIvPg0KPC9jbGlwUGF0aD4NCjwvZGVmcz4NCjwvc3ZnPg0K";
const ETHER_ICON =
"data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNjAiIGhlaWdodD0iNjAiIHZpZXdCb3g9IjYgNiA0OCA0OCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4NCjxnIGZpbHRlcj0idXJsKCNmaWx0ZXIwX2RfOTExNV80MDk4NSkiPg0KPGNpcmNsZSBjeD0iMzAiIGN5PSIzMCIgcj0iMjQiIGZpbGw9IiM2NzgwRTMiLz4NCjwvZz4NCjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMF85MTE1XzQwOTg1KSI+DQo8cGF0aCBkPSJNMzAuNDk4MiAxNUwzMC4zMTI1IDE1LjY2MDlWMzQuODM2M0wzMC40OTgyIDM1LjAzMDRMMzguOTk1NSAyOS43NjlMMzAuNDk4MiAxNVoiIGZpbGw9IiNDMkNCRjMiLz4NCjxwYXRoIGQ9Ik0zMC40OTc1IDE1TDIyIDI5Ljc2OUwzMC40OTc1IDM1LjAzMDRWMjUuNzIzMlYxNVoiIGZpbGw9IndoaXRlIi8+DQo8cGF0aCBkPSJNMzAuNDk5MiAzNi43MTU3TDMwLjM5NDUgMzYuODQ5NFY0My42Nzk5TDMwLjQ5OTIgNDQuMDAwMUwzOS4wMDE3IDMxLjQ1N0wzMC40OTkyIDM2LjcxNTdaIiBmaWxsPSIjQzBDQUYyIi8+DQo8cGF0aCBkPSJNMzAuNDk3NSA0NC4wMDAxVjM2LjcxNTdMMjIgMzEuNDU3TDMwLjQ5NzUgNDQuMDAwMVoiIGZpbGw9IndoaXRlIi8+DQo8cGF0aCBkPSJNMzAuNSAzNS4wMzAzTDM4Ljk5NzMgMjkuNzY5TDMwLjUgMjUuNzIzMVYzNS4wMzAzWiIgZmlsbD0iIzg1OThFOCIvPg0KPHBhdGggZD0iTTIyIDI5Ljc2OUwzMC40OTc1IDM1LjAzMDNWMjUuNzIzMUwyMiAyOS43NjlaIiBmaWxsPSIjQzJDQkYzIi8+DQo8L2c+DQo8ZGVmcz4NCjxmaWx0ZXIgaWQ9ImZpbHRlcjBfZF85MTE1XzQwOTg1IiB4PSIwIiB5PSIwIiB3aWR0aD0iNjAiIGhlaWdodD0iNjAiIGZpbHRlclVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgY29sb3ItaW50ZXJwb2xhdGlvbi1maWx0ZXJzPSJzUkdCIj4NCjxmZUZsb29kIGZsb29kLW9wYWNpdHk9IjAiIHJlc3VsdD0iQmFja2dyb3VuZEltYWdlRml4Ii8+DQo8ZmVDb2xvck1hdHJpeCBpbj0iU291cmNlQWxwaGEiIHR5cGU9Im1hdHJpeCIgdmFsdWVzPSIwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAxMjcgMCIgcmVzdWx0PSJoYXJkQWxwaGEiLz4NCjxmZU9mZnNldC8+DQo8ZmVHYXVzc2lhbkJsdXIgc3RkRGV2aWF0aW9uPSIzIi8+DQo8ZmVDb21wb3NpdGUgaW4yPSJoYXJkQWxwaGEiIG9wZXJhdG9yPSJvdXQiLz4NCjxmZUNvbG9yTWF0cml4IHR5cGU9Im1hdHJpeCIgdmFsdWVzPSIwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwLjA3IDAiLz4NCjxmZUJsZW5kIG1vZGU9Im5vcm1hbCIgaW4yPSJCYWNrZ3JvdW5kSW1hZ2VGaXgiIHJlc3VsdD0iZWZmZWN0MV9kcm9wU2hhZG93XzkxMTVfNDA5ODUiLz4NCjxmZUJsZW5kIG1vZGU9Im5vcm1hbCIgaW49IlNvdXJjZUdyYXBoaWMiIGluMj0iZWZmZWN0MV9kcm9wU2hhZG93XzkxMTVfNDA5ODUiIHJlc3VsdD0ic2hhcGUiLz4NCjwvZmlsdGVyPg0KPGNsaXBQYXRoIGlkPSJjbGlwMF85MTE1XzQwOTg1Ij4NCjxyZWN0IHdpZHRoPSIxNyIgaGVpZ2h0PSIyOSIgZmlsbD0id2hpdGUiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDIyIDE1KSIvPg0KPC9jbGlwUGF0aD4NCjwvZGVmcz4NCjwvc3ZnPg0K";
interface ChainInfo {
currencyIcon?: string;
icon?: string;
Expand Down Expand Up @@ -29,7 +30,8 @@ export const CHAIN_INFO: Record<number, ChainInfo> = {
wrappedToken: "0x094616f0bdfb0b526bd735bf66eca0ad254ca81f",
},
100: {
currencyIcon: "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPCFET0NUWVBFIHN2ZyBQVUJMSUMgIi0vL1czQy8vRFREIFNWRyAxLjEvL0VOIiAiaHR0cDovL3d3dy53My5vcmcvR3JhcGhpY3MvU1ZHLzEuMS9EVEQvc3ZnMTEuZHRkIj4KPHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZlcnNpb249IjEuMSIgd2lkdGg9IjM3NHB4IiBoZWlnaHQ9IjM3NHB4IiBzdHlsZT0ic2hhcGUtcmVuZGVyaW5nOmdlb21ldHJpY1ByZWNpc2lvbjsgdGV4dC1yZW5kZXJpbmc6Z2VvbWV0cmljUHJlY2lzaW9uOyBpbWFnZS1yZW5kZXJpbmc6b3B0aW1pemVRdWFsaXR5OyBmaWxsLXJ1bGU6ZXZlbm9kZDsgY2xpcC1ydWxlOmV2ZW5vZGQiIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIj4KPGc+PHBhdGggc3R5bGU9Im9wYWNpdHk6MSIgZmlsbD0iIzAwMzM3MCIgZD0iTSA0Mi41LC0wLjUgQyAxMzguNSwtMC41IDIzNC41LC0wLjUgMzMwLjUsLTAuNUMgMzUzLjE2Nyw1LjUgMzY3LjUsMTkuODMzMyAzNzMuNSw0Mi41QyAzNzMuNSwxMzguNSAzNzMuNSwyMzQuNSAzNzMuNSwzMzAuNUMgMzY3LjUsMzUzLjE2NyAzNTMuMTY3LDM2Ny41IDMzMC41LDM3My41QyAyMzQuNSwzNzMuNSAxMzguNSwzNzMuNSA0Mi41LDM3My41QyAxOS44MzMzLDM2Ny41IDUuNSwzNTMuMTY3IC0wLjUsMzMwLjVDIC0wLjUsMjM0LjUgLTAuNSwxMzguNSAtMC41LDQyLjVDIDUuNSwxOS44MzMzIDE5LjgzMzMsNS41IDQyLjUsLTAuNSBaIi8+PC9nPgo8Zz48cGF0aCBzdHlsZT0ib3BhY2l0eToxIiBmaWxsPSIjMGFlMTNhIiBkPSJNIDE4Ny41LDE0My41IEMgMTk1LjgzMywxNTIuMTY3IDIwNC4xNjcsMTYwLjgzMyAyMTIuNSwxNjkuNUMgMjE5LjcxNCwxODIuMzI2IDIxOC44OCwxOTQuNjU5IDIxMCwyMDYuNUMgMjAyLjI3MiwyMTQuMzk2IDE5NC40MzgsMjIyLjA2MyAxODYuNSwyMjkuNUMgMTY5LjM2OCwyNDcuMTMyIDE1Mi4wMzUsMjY0LjYzMiAxMzQuNSwyODJDIDEyMS45MzIsMjkxLjE4MSAxMDguOTMyLDI5MS44NDggOTUuNSwyODRDIDgyLjU0MzEsMjcyLjQzNCA4MC4wNDMxLDI1OC45MzQgODgsMjQzLjVDIDEwNC4zMDQsMjI2LjE5NSAxMjAuOTcxLDIwOS4xOTUgMTM4LDE5Mi41QyAxNDAuNzg2LDE4OS4wMzMgMTQxLjEyLDE4NS4zNjYgMTM5LDE4MS41QyAxMjIuNjY3LDE2NS4xNjcgMTA2LjMzMywxNDguODMzIDkwLDEzMi41QyA3OS44MjQ5LDExNi40MDYgODEuNjU4MiwxMDEuOTA2IDk1LjUsODlDIDEwOC45Niw4MS4xMTk5IDEyMS45Niw4MS43ODY1IDEzNC41LDkxQyAxNTIuMDM1LDEwOC43MDIgMTY5LjcwMSwxMjYuMjAyIDE4Ny41LDE0My41IFoiLz48L2c+CjxnPjxwYXRoIHN0eWxlPSJvcGFjaXR5OjEiIGZpbGw9IiMwYWUxM2EiIGQ9Ik0gMjc2LjUsMTM5LjUgQyAyNjAuMDA1LDE0Ny4wMzYgMjQ2LjE3MiwxNDMuNzAyIDIzNSwxMjkuNUMgMjI5LjMxMywxMTguODA5IDIyOS4xNDYsMTA4LjE0MyAyMzQuNSw5Ny41QyAyNDYuNjU1LDgyLjEzNyAyNjEuMzIyLDc5LjMwMzcgMjc4LjUsODlDIDI5MS45OTQsMTAxLjMyMiAyOTQuMTYxLDExNS40ODkgMjg1LDEzMS41QyAyODIuMjcxLDEzNC4zOTkgMjc5LjQzOCwxMzcuMDY2IDI3Ni41LDEzOS41IFoiLz48L2c+CjxnPjxwYXRoIHN0eWxlPSJvcGFjaXR5OjEiIGZpbGw9IiMwNDc4NWIiIGQ9Ik0gMjM0LjUsOTcuNSBDIDIyOS4xNDYsMTA4LjE0MyAyMjkuMzEzLDExOC44MDkgMjM1LDEyOS41QyAyNDYuMTcyLDE0My43MDIgMjYwLjAwNSwxNDcuMDM2IDI3Ni41LDEzOS41QyAyNjIuODY5LDE1My42MzIgMjQ5LjAzNSwxNjcuNjMyIDIzNSwxODEuNUMgMjMzLjE4MiwxODQuNDE2IDIzMy4wMTYsMTg3LjQxNiAyMzQuNSwxOTAuNUMgMjI3LjE2NywxODMuNSAyMTkuODMzLDE3Ni41IDIxMi41LDE2OS41QyAyMDQuMTY3LDE2MC44MzMgMTk1LjgzMywxNTIuMTY3IDE4Ny41LDE0My41QyAyMDMuMTQsMTI4LjE5NCAyMTguODA3LDExMi44NjEgMjM0LjUsOTcuNSBaIi8+PC9nPgo8Zz48cGF0aCBzdHlsZT0ib3BhY2l0eToxIiBmaWxsPSIjMGRiNDBiIiBkPSJNIDIxMi41LDE2OS41IEMgMjE5LjgzMywxNzYuNSAyMjcuMTY3LDE4My41IDIzNC41LDE5MC41QyAyNTEuMTMyLDIwNy42MzIgMjY3Ljk2NSwyMjQuNjMyIDI4NSwyNDEuNUMgMjk0LjA2NywyNTcuNDIgMjkxLjkwMSwyNzEuNTg3IDI3OC41LDI4NEMgMjY3LjA1NSwyOTAuOTM1IDI1NS4zODksMjkxLjI2OSAyNDMuNSwyODVDIDIyNC45MzYsMjY1LjkzNCAyMDUuOTM2LDI0Ny40MzQgMTg2LjUsMjI5LjVDIDE5NC40MzgsMjIyLjA2MyAyMDIuMjcyLDIxNC4zOTYgMjEwLDIwNi41QyAyMTguODgsMTk0LjY1OSAyMTkuNzE0LDE4Mi4zMjYgMjEyLjUsMTY5LjUgWiIvPjwvZz4KPC9zdmc+Cg==",
currencyIcon:
"data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPCFET0NUWVBFIHN2ZyBQVUJMSUMgIi0vL1czQy8vRFREIFNWRyAxLjEvL0VOIiAiaHR0cDovL3d3dy53My5vcmcvR3JhcGhpY3MvU1ZHLzEuMS9EVEQvc3ZnMTEuZHRkIj4KPHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZlcnNpb249IjEuMSIgd2lkdGg9IjM3NHB4IiBoZWlnaHQ9IjM3NHB4IiBzdHlsZT0ic2hhcGUtcmVuZGVyaW5nOmdlb21ldHJpY1ByZWNpc2lvbjsgdGV4dC1yZW5kZXJpbmc6Z2VvbWV0cmljUHJlY2lzaW9uOyBpbWFnZS1yZW5kZXJpbmc6b3B0aW1pemVRdWFsaXR5OyBmaWxsLXJ1bGU6ZXZlbm9kZDsgY2xpcC1ydWxlOmV2ZW5vZGQiIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIj4KPGc+PHBhdGggc3R5bGU9Im9wYWNpdHk6MSIgZmlsbD0iIzAwMzM3MCIgZD0iTSA0Mi41LC0wLjUgQyAxMzguNSwtMC41IDIzNC41LC0wLjUgMzMwLjUsLTAuNUMgMzUzLjE2Nyw1LjUgMzY3LjUsMTkuODMzMyAzNzMuNSw0Mi41QyAzNzMuNSwxMzguNSAzNzMuNSwyMzQuNSAzNzMuNSwzMzAuNUMgMzY3LjUsMzUzLjE2NyAzNTMuMTY3LDM2Ny41IDMzMC41LDM3My41QyAyMzQuNSwzNzMuNSAxMzguNSwzNzMuNSA0Mi41LDM3My41QyAxOS44MzMzLDM2Ny41IDUuNSwzNTMuMTY3IC0wLjUsMzMwLjVDIC0wLjUsMjM0LjUgLTAuNSwxMzguNSAtMC41LDQyLjVDIDUuNSwxOS44MzMzIDE5LjgzMzMsNS41IDQyLjUsLTAuNSBaIi8+PC9nPgo8Zz48cGF0aCBzdHlsZT0ib3BhY2l0eToxIiBmaWxsPSIjMGFlMTNhIiBkPSJNIDE4Ny41LDE0My41IEMgMTk1LjgzMywxNTIuMTY3IDIwNC4xNjcsMTYwLjgzMyAyMTIuNSwxNjkuNUMgMjE5LjcxNCwxODIuMzI2IDIxOC44OCwxOTQuNjU5IDIxMCwyMDYuNUMgMjAyLjI3MiwyMTQuMzk2IDE5NC40MzgsMjIyLjA2MyAxODYuNSwyMjkuNUMgMTY5LjM2OCwyNDcuMTMyIDE1Mi4wMzUsMjY0LjYzMiAxMzQuNSwyODJDIDEyMS45MzIsMjkxLjE4MSAxMDguOTMyLDI5MS44NDggOTUuNSwyODRDIDgyLjU0MzEsMjcyLjQzNCA4MC4wNDMxLDI1OC45MzQgODgsMjQzLjVDIDEwNC4zMDQsMjI2LjE5NSAxMjAuOTcxLDIwOS4xOTUgMTM4LDE5Mi41QyAxNDAuNzg2LDE4OS4wMzMgMTQxLjEyLDE4NS4zNjYgMTM5LDE4MS41QyAxMjIuNjY3LDE2NS4xNjcgMTA2LjMzMywxNDguODMzIDkwLDEzMi41QyA3OS44MjQ5LDExNi40MDYgODEuNjU4MiwxMDEuOTA2IDk1LjUsODlDIDEwOC45Niw4MS4xMTk5IDEyMS45Niw4MS43ODY1IDEzNC41LDkxQyAxNTIuMDM1LDEwOC43MDIgMTY5LjcwMSwxMjYuMjAyIDE4Ny41LDE0My41IFoiLz48L2c+CjxnPjxwYXRoIHN0eWxlPSJvcGFjaXR5OjEiIGZpbGw9IiMwYWUxM2EiIGQ9Ik0gMjc2LjUsMTM5LjUgQyAyNjAuMDA1LDE0Ny4wMzYgMjQ2LjE3MiwxNDMuNzAyIDIzNSwxMjkuNUMgMjI5LjMxMywxMTguODA5IDIyOS4xNDYsMTA4LjE0MyAyMzQuNSw5Ny41QyAyNDYuNjU1LDgyLjEzNyAyNjEuMzIyLDc5LjMwMzcgMjc4LjUsODlDIDI5MS45OTQsMTAxLjMyMiAyOTQuMTYxLDExNS40ODkgMjg1LDEzMS41QyAyODIuMjcxLDEzNC4zOTkgMjc5LjQzOCwxMzcuMDY2IDI3Ni41LDEzOS41IFoiLz48L2c+CjxnPjxwYXRoIHN0eWxlPSJvcGFjaXR5OjEiIGZpbGw9IiMwNDc4NWIiIGQ9Ik0gMjM0LjUsOTcuNSBDIDIyOS4xNDYsMTA4LjE0MyAyMjkuMzEzLDExOC44MDkgMjM1LDEyOS41QyAyNDYuMTcyLDE0My43MDIgMjYwLjAwNSwxNDcuMDM2IDI3Ni41LDEzOS41QyAyNjIuODY5LDE1My42MzIgMjQ5LjAzNSwxNjcuNjMyIDIzNSwxODEuNUMgMjMzLjE4MiwxODQuNDE2IDIzMy4wMTYsMTg3LjQxNiAyMzQuNSwxOTAuNUMgMjI3LjE2NywxODMuNSAyMTkuODMzLDE3Ni41IDIxMi41LDE2OS41QyAyMDQuMTY3LDE2MC44MzMgMTk1LjgzMywxNTIuMTY3IDE4Ny41LDE0My41QyAyMDMuMTQsMTI4LjE5NCAyMTguODA3LDExMi44NjEgMjM0LjUsOTcuNSBaIi8+PC9nPgo8Zz48cGF0aCBzdHlsZT0ib3BhY2l0eToxIiBmaWxsPSIjMGRiNDBiIiBkPSJNIDIxMi41LDE2OS41IEMgMjE5LjgzMywxNzYuNSAyMjcuMTY3LDE4My41IDIzNC41LDE5MC41QyAyNTEuMTMyLDIwNy42MzIgMjY3Ljk2NSwyMjQuNjMyIDI4NSwyNDEuNUMgMjk0LjA2NywyNTcuNDIgMjkxLjkwMSwyNzEuNTg3IDI3OC41LDI4NEMgMjY3LjA1NSwyOTAuOTM1IDI1NS4zODksMjkxLjI2OSAyNDMuNSwyODVDIDIyNC45MzYsMjY1LjkzNCAyMDUuOTM2LDI0Ny40MzQgMTg2LjUsMjI5LjVDIDE5NC40MzgsMjIyLjA2MyAyMDIuMjcyLDIxNC4zOTYgMjEwLDIwNi41QyAyMTguODgsMTk0LjY1OSAyMTkuNzE0LDE4Mi4zMjYgMjEyLjUsMTY5LjUgWiIvPjwvZz4KPC9zdmc+Cg==",
icon: "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDI2LjAuMSwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHZpZXdCb3g9IjAgMCA0MjggNDI4IiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCA0MjggNDI4OyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+CjxwYXRoIHN0eWxlPSJmaWxsOiMwMDE5M0M7IiBkPSJNMTI1LjgsMjQzLjdjMTIuMywwLDI0LjMtNC4xLDM0LTExLjZsLTc4LTc4Yy0xOC44LDI0LjMtMTQuMyw1OS4zLDEwLDc4LjEKCUMxMDEuNiwyMzkuNiwxMTMuNSwyNDMuNywxMjUuOCwyNDMuN0wxMjUuOCwyNDMuN3oiLz4KPHBhdGggc3R5bGU9ImZpbGw6IzAwMTkzQzsiIGQ9Ik0zNTcuOCwxODhjMC0xMi4zLTQuMS0yNC4zLTExLjYtMzRsLTc4LDc4YzI0LjMsMTguOCw1OS4yLDE0LjMsNzgtMTAKCUMzNTMuNywyMTIuMywzNTcuOCwyMDAuMywzNTcuOCwxODh6Ii8+CjxwYXRoIHN0eWxlPSJmaWxsOiMwMDE5M0M7IiBkPSJNMzk3LjEsMTAzLjFsLTM0LjUsMzQuNWMyNy44LDMzLjMsMjMuNCw4Mi45LTkuOSwxMTAuN2MtMjkuMiwyNC40LTcxLjYsMjQuNC0xMDAuOCwwTDIxNCwyODYuMgoJbC0zNy44LTM3LjhjLTMzLjMsMjcuOC04Mi45LDIzLjQtMTEwLjctOS45Yy0yNC40LTI5LjItMjQuNC03MS42LDAtMTAwLjhMNDcuOCwxMjBMMzEsMTAzLjFDMTAuNywxMzYuNSwwLDE3NC45LDAsMjE0CgljMCwxMTguMiw5NS44LDIxNCwyMTQsMjE0czIxNC05NS44LDIxNC0yMTRDNDI4LjEsMTc0LjksNDE3LjMsMTM2LjUsMzk3LjEsMTAzLjF6Ii8+CjxwYXRoIHN0eWxlPSJmaWxsOiMwMDE5M0M7IiBkPSJNMzY4LjgsNjYuM2MtODEuNS04NS41LTIxNi45LTg4LjctMzAyLjQtNy4yYy0yLjUsMi40LTQuOSw0LjgtNy4yLDcuMmMtNS4zLDUuNi0xMC4zLDExLjQtMTUsMTcuNQoJTDIxNCwyNTMuN0wzODMuOCw4My44QzM3OS4yLDc3LjcsMzc0LjEsNzEuOSwzNjguOCw2Ni4zeiBNMjE0LDI4YzUwLDAsOTYuNiwxOS4zLDEzMS42LDU0LjVMMjE0LDIxNC4xTDgyLjQsODIuNQoJQzExNy40LDQ3LjMsMTY0LDI4LDIxNCwyOHoiLz4KPC9zdmc+Cg==",
testnet: false,
wrappedToken: "0x6a023ccd1ff6f2045c3309768ead9e68f978f6e1",
Expand Down
14 changes: 13 additions & 1 deletion src/mpcContract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ interface MpcContractInterface extends Contract {
* High-level interface for the Near MPC-Recovery Contract
* located in: https://github.com/near/mpc-recovery
*/
export class MpcContract {
export class MpcContract implements IMpcContract {
contract: MpcContractInterface;
connectedAccount: Account;

Expand Down Expand Up @@ -142,3 +142,15 @@ function gasOrDefault(gas?: bigint): string {
// Default of 250 TGAS
return (TGAS * 250n).toString();
}

export interface IMpcContract {
connectedAccount: Account;
accountId(): string;
deriveEthAddress(derivationPath: string): Promise<Address>;
getDeposit(): Promise<string>;
requestSignature(signArgs: SignArgs, gas?: bigint): Promise<Signature>;
encodeSignatureRequestTx(
signArgs: SignArgs,
gas?: bigint
): Promise<FunctionCallTransaction<{ request: SignArgs }>>;
}
4 changes: 2 additions & 2 deletions src/types.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { MpcContract } from "./mpcContract";
import { IMpcContract } from "./mpcContract";
import {
Address,
Hex,
Expand Down Expand Up @@ -48,7 +48,7 @@ export interface BaseTx {
* @property {string} [derivationPath] - Path used to generate ETH account from NEAR account (e.g., "ethereum,1").
*/
export interface AdapterParams {
mpcContract: MpcContract;
mpcContract: IMpcContract;
derivationPath?: string;
}

Expand Down
2 changes: 2 additions & 0 deletions src/utils/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
export * from "./request";
export * from "./signature";
export * from "./transaction";

export { mockAdapter } from "./mock-sign";
106 changes: 106 additions & 0 deletions src/utils/mock-sign.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import { Address, Hex, Signature, toHex } from "viem";
import { PrivateKeyAccount, privateKeyToAccount } from "viem/accounts";
import { FunctionCallTransaction, SignArgs } from "../types";
import { Account } from "near-api-js";
import { IMpcContract, nearAccountFromAccountId, NearEthAdapter } from "..";

function fromPayload(payload: number[]): Hex {
if (payload.length !== 32) {
throw new Error(`Payload must have 32 bytes: ${payload}`);
}
// Convert number[] back to Uint8Array
return toHex(new Uint8Array(payload));
}

/**
* Converts a raw hexadecimal signature into a structured Signature object
* @param hexSignature The raw hexadecimal signature (e.g., '0x...')
* @returns A structured Signature object with fields r, s, v, and yParity
*/
function hexToSignature(hexSignature: Hex): Signature {
// Strip "0x" prefix if it exists
const cleanedHex = hexSignature.slice(2);

// Ensure the signature is 65 bytes (130 hex characters)
if (cleanedHex.length !== 130) {
throw new Error(
`Invalid hex signature length: ${cleanedHex.length}. Expected 130 characters (65 bytes).`
);
}

// Extract the r, s, and v components from the hex signature
const v = BigInt(`0x${cleanedHex.slice(128, 130)}`); // Last byte (2 hex characters)
return {
r: `0x${cleanedHex.slice(0, 64)}`, // First 32 bytes (64 hex characters)
s: `0x${cleanedHex.slice(64, 128)}`, // Next 32 bytes (64 hex characters),
v,
// Determine yParity based on v (27 or 28 maps to 0 or 1)
yParity: v === 27n ? 0 : v === 28n ? 1 : undefined,
};
}

export class MockMpcContract implements IMpcContract {
connectedAccount: Account;
private ethAccount: PrivateKeyAccount;

constructor(account: Account, privateKey?: Hex) {
this.connectedAccount = account;
this.ethAccount = privateKeyToAccount(
privateKey ||
// Known key from deterministic ganache client
"0x4f3edf983ac636a65a842ce7c78d9aa706d3b113bce9c46f30d7d21715b23b1d"
);
}

accountId(): string {
return "mock-mpc.offline";
}

deriveEthAddress = async (_unused?: string): Promise<Address> => {
return this.ethAccount.address;
};

getDeposit = async (): Promise<string> => {
return "1";
};

requestSignature = async (
signArgs: SignArgs,
_gas?: bigint
): Promise<Signature> => {
const hexSignature = await this.ethAccount.sign({
hash: fromPayload(signArgs.payload),
});
return hexToSignature(hexSignature);
};

async encodeSignatureRequestTx(
signArgs: SignArgs,
gas?: bigint
): Promise<FunctionCallTransaction<{ request: SignArgs }>> {
return {
signerId: this.connectedAccount.accountId,
receiverId: this.accountId(),
actions: [
{
type: "FunctionCall",
params: {
methodName: "sign",
args: { request: signArgs },
gas: gas ? gas.toString() : "1",
deposit: await this.getDeposit(),
},
},
],
};
}
}

export async function mockAdapter(privateKey?: Hex): Promise<NearEthAdapter> {
const account = await nearAccountFromAccountId("mock-user.offline", {
networkId: "testnet",
nodeUrl: "https://rpc.testnet.near.org",
});
const mpcContract = new MockMpcContract(account, privateKey);
return NearEthAdapter.fromConfig({ mpcContract });
}
Loading

0 comments on commit 0c11b97

Please sign in to comment.