Skip to content

Commit

Permalink
fix(recover): update snjs, rpc and add basic cairo 1 recovery
Browse files Browse the repository at this point in the history
  • Loading branch information
simonheys committed Apr 10, 2024
1 parent fcfe8fa commit 740d984
Show file tree
Hide file tree
Showing 20 changed files with 2,759 additions and 111 deletions.
1 change: 1 addition & 0 deletions .nvmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
20.11.1
17 changes: 17 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "launch",
"runtimeExecutable": "yarn",
"runtimeArgs": ["ts-node", "index.ts"],
"name": "Launch Program",
"skipFiles": ["<node_internals>/**"],
"console": "integratedTerminal"
}
]
}
14 changes: 12 additions & 2 deletions Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,16 @@ You can then choose to fix issues and recover your funds to a new account addres
It is simplest to download the latest release from the [releases page](https://github.com/argentlabs/argent-starknet-recover/releases).
After downloading the binary matching your machine you can run it inside a terminal.

If you have Node installed you can also clone this repo and run `yarn && yarn start` to run the CLI tool.
If you have `node`, `nvm` and `yarn` installed you can also clone this repo and run the following:

**Use at your own risks**
```bash
$ nvm use
$ yarn
$ yarn start
```

**Use at your own risk**

## Debugging with VSCode

You can use the Run and Debug feature to debug with VSCode - good luck!
35 changes: 14 additions & 21 deletions actions/transferAll/core.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { BigNumber, utils } from "ethers";
import { number, uint256 } from "starknet";
import { compileCalldata } from "starknet/dist/utils/stark";
import { utils } from "ethers";
import { num, CallData } from "starknet";
import { number, uint256 } from "starknet-410";
import { Account } from "../../ui/pickAccounts";
import TOKENS from "../../default-tokens.json";
import { Ora } from "ora";
Expand All @@ -22,24 +22,20 @@ export async function transferAll(acc: Account, newAddress: string, ora: Ora) {
return {
contractAddress: token,
entrypoint: "transfer",
calldata: compileCalldata({
calldata: CallData.compile({
to: newAddress.toLowerCase(),
value: {
type: "struct",
...uint256.bnToUint256(
number.toBN(
utils
.parseUnits(balance, tokenDetails?.decimals || 18)
.toString()
)
),
},
value: uint256.bnToUint256(
number.toBN(
utils.parseUnits(balance, tokenDetails?.decimals || 18).toString()
)
),
}),
};
});

if (calls.length) {
const { suggestedMaxFee } = await estimateFee(acc, calls);
const result = await estimateFee(acc, calls);
const suggestedMaxFee = result.suggestedMaxFee || result.overall_fee;

const callsWithFee = calls
.map((c) => {
Expand All @@ -48,7 +44,7 @@ export async function transferAll(acc: Account, newAddress: string, ora: Ora) {
const balance = acc.balances[c.contractAddress];
const amount = utils
.parseUnits(balance, tokenDetails?.decimals ?? 18)
.sub(number.toHex(suggestedMaxFee));
.sub(num.toHex(suggestedMaxFee));

if (amount.lte(0)) {
ora.info(
Expand All @@ -61,12 +57,9 @@ export async function transferAll(acc: Account, newAddress: string, ora: Ora) {

return {
...c,
calldata: compileCalldata({
calldata: CallData.compile({
to: newAddress.toLowerCase(),
value: {
type: "struct",
...uint256.bnToUint256(number.toBN(amount.toString())),
},
value: uint256.bnToUint256(number.toBN(amount.toString())),
}),
};
}
Expand Down
2 changes: 1 addition & 1 deletion addressFormatting.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { encode } from "starknet";
import { encode } from "starknet-410";

export const formatAddress = (address: string) =>
encode.addHexPrefix(encode.removeHexPrefix(address).padStart(64, "0"));
Expand Down
Binary file removed argent-x-multicall-4.8.7.tgz
Binary file not shown.
113 changes: 92 additions & 21 deletions execute.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
import {
RpcProvider as NewRpcProvider,
Account as NewAccount,
} from "starknet-4220";
import { Account as LatestAccount } from "starknet";
import { Account as Account4220 } from "starknet-4220";
import {
Call,
RpcProvider as OldRpcProvider,
Account as OldAccount,
ec,
} from "starknet";
} from "starknet-410";
import { BigNumber } from "ethers";
import { Account } from "./ui/pickAccounts";
import { lte } from "semver";
import { getRpcNodeUrlForNetworkId } from "./getProvider";
import {
getProvider4220ForNetworkId,
getProviderForNetworkId,
getRpcNodeUrlForNetworkId,
} from "./getProvider";

export async function estimateFee(account: Account, call: Call[] | Call) {
const calls = Array.isArray(call) ? call : [call];
Expand All @@ -24,15 +26,43 @@ export async function estimateFee(account: Account, call: Call[] | Call) {
);
}
const nodeUrl = getRpcNodeUrlForNetworkId(account.networkId);

try {
const oldRpcProvider = new OldRpcProvider({ nodeUrl });
const a = new OldAccount(oldRpcProvider, lowerCaseAddress, keyPair);
if (!account.privateKey) {
throw new Error("Account private key is missing");
}
const provider = getProviderForNetworkId(account.networkId);
const a = new LatestAccount(provider, lowerCaseAddress, account.privateKey);
return await a.estimateFee(calls);
} catch {
const newRpcProvider = new NewRpcProvider({ nodeUrl });
const a = new NewAccount(newRpcProvider, lowerCaseAddress, keyPair);
} catch (e) {
console.warn(
`Fallback to old provider - estimateFee error ${e}`,
(e as any)?.errorCode
);
}

try {
const provider = getProvider4220ForNetworkId(account.networkId);
const a = new Account4220(provider, lowerCaseAddress, keyPair);
return a.estimateFee(calls);
} catch (e) {
console.warn(
`Fallback to old provider - estimateFee error ${e}`,
(e as any)?.errorCode
);
}

try {
const provider = new OldRpcProvider({ nodeUrl });
const a = new OldAccount(provider, lowerCaseAddress, keyPair);
return await a.estimateFee(calls);
} catch (e) {
console.warn(
`Oldest provider failed - estimateFee error ${e}`,
(e as any)?.errorCode
);
}
throw new Error("Estimate fee failed");
}

export async function execute(account: Account, call: Call[] | Call) {
Expand All @@ -45,28 +75,69 @@ export async function execute(account: Account, call: Call[] | Call) {
"Account cant be controlled with the selected private key or seed"
);
}

const nodeUrl = getRpcNodeUrlForNetworkId(account.networkId);
if (account.version && lte(account.version, "0.2.2")) {
try {
const oldRpcProvider = new OldRpcProvider({ nodeUrl });
const a = new OldAccount(oldRpcProvider, lowerCaseAddress, keyPair);
const provider = new OldRpcProvider({ nodeUrl });
const a = new OldAccount(provider, lowerCaseAddress, keyPair);
return await a.execute(calls);
} catch (e) {
console.warn("Fallback to new provider", (e as any)?.errorCode);
const newRpcProvider = new NewRpcProvider({ nodeUrl });
const a = new NewAccount(newRpcProvider, lowerCaseAddress, keyPair);
console.warn(
`Fallback to old provider - estimateFee error ${e}`,
(e as any)?.errorCode
);
}
try {
const provider = getProvider4220ForNetworkId(account.networkId);
const a = new Account4220(provider, lowerCaseAddress, keyPair);
return await a.execute(calls);
} catch (e) {
console.warn(
`Oldest provider failed - execute error ${e}`,
(e as any)?.errorCode
);
}
} else {
try {
const newRpcProvider = new NewRpcProvider({ nodeUrl });
const a = new NewAccount(newRpcProvider, lowerCaseAddress, keyPair);
if (!account.privateKey) {
throw new Error("Account private key is missing");
}
const provider = getProviderForNetworkId(account.networkId);
const a = new LatestAccount(
provider,
lowerCaseAddress,
account.privateKey
);
return await a.execute(calls);
} catch (e) {
console.warn("Fallback to old provider", (e as any)?.errorCode);
const oldRpcProvider = new OldRpcProvider({ nodeUrl });
const a = new OldAccount(oldRpcProvider, lowerCaseAddress, keyPair);
console.warn(
`Fallback to older provider - execute error ${e}`,
(e as any)?.errorCode
);
}

try {
const provider = getProvider4220ForNetworkId(account.networkId);
const a = new Account4220(provider, lowerCaseAddress, keyPair);
return await a.execute(calls);
} catch (e) {
console.warn(
`Fallback to older provider - execute error ${e}`,
(e as any)?.errorCode
);
}

try {
const provider = new OldRpcProvider({ nodeUrl });
const a = new OldAccount(provider, lowerCaseAddress, keyPair);
return await a.execute(calls);
} catch (e) {
console.warn(
`Oldest provider failed - execute error ${e}`,
(e as any)?.errorCode
);
}
}
throw new Error("Execute transation failed");
}
2 changes: 1 addition & 1 deletion genSigners.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { BigNumber, Wallet } from "ethers";
import { ec, number } from "starknet";
import { ec, number } from "starknet-410";
import { BASE_DERIVATION_PATHS } from "./getAccounts";
import { getPathForIndex, getStarkPair } from "./keyDerivation";

Expand Down
24 changes: 20 additions & 4 deletions getAccounts.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { Wallet } from "ethers";
import { ec, hash, number, stark } from "starknet";
import { ec, hash, number, stark } from "starknet-410";
import { getBalances } from "./getTokenBalance";
import { getPathForIndex, getStarkPair } from "./keyDerivation";
import { getProviderForNetworkId } from "./getProvider";
import { getProvider4220ForNetworkId } from "./getProvider";
import { NetworkId } from "./types";

const CHECK_OFFSET = 10;
Expand All @@ -17,6 +17,12 @@ const ARGENT_ACCOUNT_CONTRACT_CLASS_HASHES = [
"0x33434ad846cdd5f23eb73ff09fe6fddd568284a0fb7d1be20ee482f044dabe2",
];

const ARGENT_ACCOUNT_CONTRACT_CLASS_HASHES_CAIRO_1 = [
"0x29927c8af6bccf3f6fda035981e765a7bdbf18a2dc0d630494f8758aa908e2b",
"0x02fadbf77a721b94bdcc3032d86a8921661717fa55145bccf88160ee2a5efcd1",
"0x1a736d6ed154502257f02b1ccdf4d9d1089f80811cd6acad48e6b6a9d1f2003",
];

export const BASE_DERIVATION_PATHS = [
"m/44'/9004'/0'/0",
"m/2645'/1195502025'/1148870696'/0'/0'",
Expand All @@ -28,7 +34,7 @@ async function getAccountByKeyPair(
contractClassHash: string,
accountClassHash: string
) {
const provider = getProviderForNetworkId(networkId);
const provider4220 = getProvider4220ForNetworkId(networkId);

const starkPub = ec.getStarkKey(keyPair);

Expand Down Expand Up @@ -58,7 +64,7 @@ async function getAccountByKeyPair(
};
}

const code = await provider.getCode(address);
const code = await provider4220.getCode(address);

if (code.bytecode.length > 0) {
return {
Expand Down Expand Up @@ -90,6 +96,16 @@ export async function getAccountsBySeedPhrase(
)
);

BASE_DERIVATION_PATHS.forEach((dp) => {
ARGENT_ACCOUNT_CONTRACT_CLASS_HASHES_CAIRO_1.forEach((accountClassHash) => {
proxyClassHashAndAccountClassHash2DMap.push([
accountClassHash,
accountClassHash,
dp,
]);
});
});

const accounts: {
address: string;
deployImplementation?: string;
Expand Down
30 changes: 22 additions & 8 deletions getImplementation.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,35 @@
import { Multicall } from "@argent/x-multicall";
import { getProviderForNetworkId } from "./getProvider";
import {
getProviderForNetworkId,
getRpcBatchProviderForNetworkId,
} from "./getProvider";
import { NetworkId } from "./types";

export async function getImplementation(
addresses: string[],
networkId: NetworkId
) {
const provider = getProviderForNetworkId(networkId);
const multicallProvider = new Multicall(provider as any);
const multicall = getRpcBatchProviderForNetworkId(networkId);

const implementationAnswers = await Promise.allSettled(
addresses.map((address) =>
multicallProvider.call({
addresses.map(async (address) => {
const get_implementation = await multicall.callContract({
contractAddress: address,
entrypoint: "get_implementation",
})
)
});
if (get_implementation !== undefined) {
return get_implementation;
}
try {
const provider = getProviderForNetworkId(networkId);
const classHash = await provider.getClassHashAt(address);
return classHash;
} catch (e) {
console.warn("Assuming tx V1 implementation");
const TXV1_ACCOUNT_CLASS_HASH =
"0x1a736d6ed154502257f02b1ccdf4d9d1089f80811cd6acad48e6b6a9d1f2003";
return TXV1_ACCOUNT_CLASS_HASH;
}
})
);

const implementations = implementationAnswers
Expand Down
Loading

0 comments on commit 740d984

Please sign in to comment.