Skip to content

Commit af3ae92

Browse files
authored
feat(contract_manager): derive starknet account address (#1720)
1 parent ce1e0c0 commit af3ae92

File tree

2 files changed

+54
-47
lines changed

2 files changed

+54
-47
lines changed

contract_manager/src/chains.ts

Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ import { Ed25519Keypair } from "@mysten/sui.js/keypairs/ed25519";
2424
import { TokenId } from "./token";
2525
import { BN, Provider, Wallet, WalletUnlocked } from "fuels";
2626
import { FUEL_ETH_ASSET_ID } from "@pythnetwork/pyth-fuel-js";
27-
import { RpcProvider } from "starknet";
27+
import { Contract, RpcProvider, Signer, ec, shortString } from "starknet";
2828

2929
export type ChainConfig = Record<string, string> & {
3030
mainnet: boolean;
@@ -676,14 +676,43 @@ export class StarknetChain extends Chain {
676676
return new UpgradeContract256Bit(this.wormholeChainName, digest).encode();
677677
}
678678

679-
// Account address derivation on Starknet depends
680-
// on the wallet application and constructor arguments used.
681679
async getAccountAddress(privateKey: PrivateKey): Promise<string> {
682-
throw new Error("Unsupported");
680+
const ARGENT_CLASS_HASH =
681+
"0x029927c8af6bccf3f6fda035981e765a7bdbf18a2dc0d630494f8758aa908e2b";
682+
const ADDR_BOUND =
683+
0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00n;
684+
685+
function computeHashOnElements(elements: string[]): string {
686+
let hash = "0";
687+
for (const item of elements) {
688+
hash = ec.starkCurve.pedersen(hash, item);
689+
}
690+
return ec.starkCurve.pedersen(hash, elements.length);
691+
}
692+
693+
const publicKey = await new Signer("0x" + privateKey).getPubKey();
694+
695+
const value = computeHashOnElements([
696+
shortString.encodeShortString("STARKNET_CONTRACT_ADDRESS"),
697+
"0",
698+
publicKey,
699+
ARGENT_CLASS_HASH,
700+
computeHashOnElements([publicKey, "0"]),
701+
]);
702+
return (BigInt(value) % ADDR_BOUND).toString(16).padStart(64, "0");
683703
}
684704

685705
async getAccountBalance(privateKey: PrivateKey): Promise<number> {
686-
throw new Error("Unsupported");
706+
const ETH =
707+
"0x049d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7";
708+
709+
const address = await this.getAccountAddress(privateKey);
710+
const provider = this.getProvider();
711+
const tokenClassData = await provider.getClassAt(ETH);
712+
const tokenContract = new Contract(tokenClassData.abi, ETH, provider);
713+
const decimals = await tokenContract.decimals();
714+
const amount = await tokenContract.balanceOf("0x" + address);
715+
return Number(amount) / Number(10n ** decimals);
687716
}
688717

689718
getProvider(): RpcProvider {

contract_manager/src/contracts/starknet.ts

Lines changed: 20 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ export class StarknetPriceFeedContract extends PriceFeedContract {
6262
return sources.map((source) => {
6363
return {
6464
emitterChain: Number(source.emitter_chain_id),
65-
emitterAddress: source.emitter_address.toString(16),
65+
emitterAddress: source.emitter_address.toString(16).padStart(64, "0"),
6666
};
6767
});
6868
}
@@ -82,7 +82,7 @@ export class StarknetPriceFeedContract extends PriceFeedContract {
8282
async getFeeTokenAddresses(): Promise<string[]> {
8383
const contract = await this.getContractClient();
8484
const tokens: bigint[] = await contract.fee_token_addresses();
85-
return tokens.map((t) => t.toString(16));
85+
return tokens.map((t) => t.toString(16).padStart(64, "0"));
8686
}
8787

8888
/**
@@ -126,21 +126,7 @@ export class StarknetPriceFeedContract extends PriceFeedContract {
126126
senderPrivateKey: PrivateKey,
127127
vaas: Buffer[]
128128
): Promise<TxResult> {
129-
// We need the account address to send transactions.
130-
throw new Error("Use executeUpdatePriceFeedWithAddress instead");
131-
}
132-
133-
/**
134-
* Executes the update instructions contained in the VAAs using the sender credentials
135-
* @param senderPrivateKey private key of the sender in hex format without 0x prefix
136-
* @param senderAddress address of the sender's account in hex format without 0x prefix
137-
* @param vaa VAA containing price update messages to execute
138-
*/
139-
async executeUpdatePriceFeedWithAddress(
140-
senderPrivateKey: PrivateKey,
141-
senderAddress: string,
142-
vaa: Buffer
143-
): Promise<TxResult> {
129+
const senderAddress = await this.chain.getAccountAddress(senderPrivateKey);
144130
const provider = this.chain.getProvider();
145131
const contract = await this.getContractClient();
146132
const account = new Account(
@@ -155,35 +141,27 @@ export class StarknetPriceFeedContract extends PriceFeedContract {
155141
const tokenContract = new Contract(tokenClassData.abi, feeToken, provider);
156142
tokenContract.connect(account);
157143

158-
const updateData = ByteBuffer.fromBuffer(vaa);
159-
const feeAmount = await contract.get_update_fee(updateData, feeToken);
160-
const feeTx = await tokenContract.approve(this.address, feeAmount);
161-
await provider.waitForTransaction(feeTx.transaction_hash);
162-
163-
const tx = await contract.update_price_feeds(updateData);
164-
const info = await provider.waitForTransaction(tx.transaction_hash);
165-
return { id: tx.transaction_hash, info };
166-
}
167-
168-
executeGovernanceInstruction(
169-
senderPrivateKey: PrivateKey,
170-
vaa: Buffer
171-
): Promise<TxResult> {
172-
// We need the account address to send transactions.
173-
throw new Error("Use executeGovernanceInstructionWithAddress instead");
144+
const ids = [];
145+
const infos = [];
146+
for (const vaa of vaas) {
147+
const updateData = ByteBuffer.fromBuffer(vaa);
148+
const feeAmount = await contract.get_update_fee(updateData, feeToken);
149+
const feeTx = await tokenContract.approve(this.address, feeAmount);
150+
await provider.waitForTransaction(feeTx.transaction_hash);
151+
152+
const tx = await contract.update_price_feeds(updateData);
153+
const info = await provider.waitForTransaction(tx.transaction_hash);
154+
ids.push(tx.transaction_hash);
155+
infos.push(info);
156+
}
157+
return { id: ids.join(","), info: infos };
174158
}
175159

176-
/**
177-
* Executes the governance instruction contained in the VAA using the sender credentials
178-
* @param senderPrivateKey private key of the sender in hex format without 0x prefix
179-
* @param senderAddress address of the sender's account in hex format without 0x prefix
180-
* @param vaa the VAA to execute
181-
*/
182-
async executeGovernanceInstructionWithAddress(
160+
async executeGovernanceInstruction(
183161
senderPrivateKey: PrivateKey,
184-
senderAddress: string,
185162
vaa: Buffer
186163
): Promise<TxResult> {
164+
const senderAddress = await this.chain.getAccountAddress(senderPrivateKey);
187165
const provider = this.chain.getProvider();
188166
const contract = await this.getContractClient();
189167
const account = new Account(
@@ -205,7 +183,7 @@ export class StarknetPriceFeedContract extends PriceFeedContract {
205183
await contract.governance_data_source();
206184
return {
207185
emitterChain: Number(source.emitter_chain_id),
208-
emitterAddress: source.emitter_address.toString(16),
186+
emitterAddress: source.emitter_address.toString(16).padStart(64, "0"),
209187
};
210188
}
211189

0 commit comments

Comments
 (0)