Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(recover): update snjs, rpc and add basic cairo 1 recovery #14

Merged
merged 1 commit into from
Apr 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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