Skip to content

Commit cfd9b7c

Browse files
feat: lambda sstore and transfer tx (#1156)
Signed-off-by: Ivaylo Nikolov <[email protected]>
1 parent 633a745 commit cfd9b7c

21 files changed

+1497
-58
lines changed

examples/transfer_with_hooks.rs

Lines changed: 263 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,263 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
3+
use clap::Parser;
4+
use hedera::{
5+
AccountCreateTransaction, AccountId, Client, ContractCreateTransaction, ContractId, EvmHookCall, EvmHookSpec, FungibleHookCall, FungibleHookType, Hbar, HookCall, HookCreationDetails, HookExtensionPoint, LambdaEvmHook, NftHookCall, NftHookType, PrivateKey, TokenCreateTransaction, TokenMintTransaction, TokenSupplyType, TokenType, TransferTransaction
6+
};
7+
8+
#[derive(Parser, Debug)]
9+
struct Args {
10+
#[clap(long, env)]
11+
operator_account_id: AccountId,
12+
13+
#[clap(long, env)]
14+
operator_key: PrivateKey,
15+
16+
#[clap(long, env, default_value = "testnet")]
17+
hedera_network: String,
18+
}
19+
20+
const HOOK_BYTECODE: &str = "6080604052348015600e575f5ffd5b506107d18061001c5f395ff3fe608060405260043610610033575f3560e01c8063124d8b301461003757806394112e2f14610067578063bd0dd0b614610097575b5f5ffd5b610051600480360381019061004c91906106f2565b6100c7565b60405161005e9190610782565b60405180910390f35b610081600480360381019061007c91906106f2565b6100d2565b60405161008e9190610782565b60405180910390f35b6100b160048036038101906100ac91906106f2565b6100dd565b6040516100be9190610782565b60405180910390f35b5f6001905092915050565b5f6001905092915050565b5f6001905092915050565b5f604051905090565b5f5ffd5b5f5ffd5b5f5ffd5b5f60a08284031215610112576101116100f9565b5b81905092915050565b5f5ffd5b5f601f19601f8301169050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b6101658261011f565b810181811067ffffffffffffffff821117156101845761018361012f565b5b80604052505050565b5f6101966100e8565b90506101a2828261015c565b919050565b5f5ffd5b5f5ffd5b5f67ffffffffffffffff8211156101c9576101c861012f565b5b602082029050602081019050919050565b5f5ffd5b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f610207826101de565b9050919050565b610217816101fd565b8114610221575f5ffd5b50565b5f813590506102328161020e565b92915050565b5f8160070b9050919050565b61024d81610238565b8114610257575f5ffd5b50565b5f8135905061026881610244565b92915050565b5f604082840312156102835761028261011b565b5b61028d604061018d565b90505f61029c84828501610224565b5f8301525060206102af8482850161025a565b60208301525092915050565b5f6102cd6102c8846101af565b61018d565b905080838252602082019050604084028301858111156102f0576102ef6101da565b5b835b818110156103195780610305888261026e565b8452602084019350506040810190506102f2565b5050509392505050565b5f82601f830112610337576103366101ab565b5b81356103478482602086016102bb565b91505092915050565b5f67ffffffffffffffff82111561036a5761036961012f565b5b602082029050602081019050919050565b5f67ffffffffffffffff8211156103955761039461012f565b5b602082029050602081019050919050565b5f606082840312156103bb576103ba61011b565b5b6103c5606061018d565b90505f6103d484828501610224565b5f8301525060206103e784828501610224565b60208301525060406103fb8482850161025a565b60408301525092915050565b5f6104196104148461037b565b61018d565b9050808382526020820190506060840283018581111561043c5761043b6101da565b5b835b81811015610465578061045188826103a6565b84526020840193505060608101905061043e565b5050509392505050565b5f82601f830112610483576104826101ab565b5b8135610493848260208601610407565b91505092915050565b5f606082840312156104b1576104b061011b565b5b6104bb606061018d565b90505f6104ca84828501610224565b5f83015250602082013567ffffffffffffffff8111156104ed576104ec6101a7565b5b6104f984828501610323565b602083015250604082013567ffffffffffffffff81111561051d5761051c6101a7565b5b6105298482850161046f565b60408301525092915050565b5f61054761054284610350565b61018d565b9050808382526020820190506020840283018581111561056a576105696101da565b5b835b818110156105b157803567ffffffffffffffff81111561058f5761058e6101ab565b5b80860161059c898261049c565b8552602085019450505060208101905061056c565b5050509392505050565b5f82601f8301126105cf576105ce6101ab565b5b81356105df848260208601610535565b91505092915050565b5f604082840312156105fd576105fc61011b565b5b610607604061018d565b90505f82013567ffffffffffffffff811115610626576106256101a7565b5b61063284828501610323565b5f83015250602082013567ffffffffffffffff811115610655576106546101a7565b5b610661848285016105bb565b60208301525092915050565b5f604082840312156106825761068161011b565b5b61068c604061018d565b90505f82013567ffffffffffffffff8111156106ab576106aa6101a7565b5b6106b7848285016105e8565b5f83015250602082013567ffffffffffffffff8111156106da576106d96101a7565b5b6106e6848285016105e8565b60208301525092915050565b5f5f60408385031215610708576107076100f1565b5b5f83013567ffffffffffffffff811115610725576107246100f5565b5b610731858286016100fd565b925050602083013567ffffffffffffffff811115610752576107516100f5565b5b61075e8582860161066d565b9150509250929050565b5f8115159050919050565b61077c81610768565b82525050565b5f6020820190506107955f830184610773565b9291505056fea26469706673582212207dfe7723f6d6869419b1cb0619758b439da0cf4ffd9520997c40a3946299d4dc64736f6c634300081e0033";
21+
22+
async fn create_hook_contract(client: &Client) -> anyhow::Result<ContractId> {
23+
let bytecode = hex::decode(HOOK_BYTECODE)?;
24+
25+
let receipt = ContractCreateTransaction::new()
26+
.bytecode(bytecode)
27+
.gas(1_700_000)
28+
.execute(client)
29+
.await?
30+
.get_receipt(client)
31+
.await?;
32+
33+
Ok(receipt.contract_id.unwrap())
34+
}
35+
36+
#[tokio::main]
37+
async fn main() -> anyhow::Result<()> {
38+
let _ = dotenvy::dotenv();
39+
let Args {
40+
operator_account_id,
41+
operator_key,
42+
hedera_network,
43+
} = Args::parse();
44+
45+
let client = Client::for_name(&hedera_network)?;
46+
client.set_operator(operator_account_id, operator_key);
47+
48+
println!("Transfer Transaction Hooks Example Start!");
49+
50+
// Step 1: Set up prerequisites - create hook contract
51+
println!("Setting up prerequisites...");
52+
53+
let hook_contract_id = create_hook_contract(&client).await?;
54+
println!("Created hook contract: {hook_contract_id}");
55+
56+
// Create hook details
57+
let hook_id = 1;
58+
let spec = EvmHookSpec::new(Some(hook_contract_id));
59+
let lambda_hook = LambdaEvmHook::new(spec, vec![]);
60+
let hook_details = HookCreationDetails::new(
61+
HookExtensionPoint::AccountAllowanceHook,
62+
hook_id,
63+
Some(lambda_hook),
64+
);
65+
66+
// Create sender account with hook
67+
let sender_key = PrivateKey::generate_ed25519();
68+
let sender_receipt = AccountCreateTransaction::new()
69+
.set_key_without_alias(sender_key.public_key())
70+
.initial_balance(Hbar::new(10))
71+
.add_hook(hook_details.clone())
72+
.freeze_with(&client)?
73+
.sign(sender_key.clone())
74+
.execute(&client)
75+
.await?
76+
.get_receipt(&client)
77+
.await?;
78+
79+
let sender_account_id = sender_receipt.account_id.unwrap();
80+
println!("Created sender account: {sender_account_id}");
81+
82+
// Create receiver account with hook and unlimited token associations
83+
let receiver_key = PrivateKey::generate_ed25519();
84+
let receiver_receipt = AccountCreateTransaction::new()
85+
.set_key_without_alias(receiver_key.public_key())
86+
.initial_balance(Hbar::new(10))
87+
.max_automatic_token_associations(-1)
88+
.add_hook(hook_details)
89+
.execute(&client)
90+
.await?
91+
.get_receipt(&client)
92+
.await?;
93+
94+
let receiver_account_id = receiver_receipt.account_id.unwrap();
95+
println!("Created receiver account: {receiver_account_id}");
96+
97+
// Create fungible token
98+
println!("Creating fungible token...");
99+
let fungible_token_id = TokenCreateTransaction::new()
100+
.name("Example Fungible Token")
101+
.symbol("EFT")
102+
.decimals(2)
103+
.initial_supply(10_000)
104+
.treasury_account_id(sender_account_id)
105+
.admin_key(sender_key.public_key())
106+
.supply_key(sender_key.public_key())
107+
.token_type(TokenType::FungibleCommon)
108+
.token_supply_type(TokenSupplyType::Infinite)
109+
.freeze_with(&client)?
110+
.sign(sender_key.clone())
111+
.execute(&client)
112+
.await?
113+
.get_receipt(&client)
114+
.await?
115+
.token_id
116+
.unwrap();
117+
118+
println!("Created fungible token: {fungible_token_id}");
119+
120+
// Create NFT token
121+
println!("Creating NFT token...");
122+
let nft_token_id = TokenCreateTransaction::new()
123+
.name("Example NFT Token")
124+
.symbol("ENT")
125+
.treasury_account_id(sender_account_id)
126+
.admin_key(sender_key.public_key())
127+
.supply_key(sender_key.public_key())
128+
.token_type(TokenType::NonFungibleUnique)
129+
.token_supply_type(TokenSupplyType::Infinite)
130+
.freeze_with(&client)?
131+
.sign(sender_key.clone())
132+
.execute(&client)
133+
.await?
134+
.get_receipt(&client)
135+
.await?
136+
.token_id
137+
.unwrap();
138+
139+
println!("Created NFT token: {nft_token_id}");
140+
141+
// Mint NFT
142+
println!("Minting NFT...");
143+
let nft_serial = TokenMintTransaction::new()
144+
.token_id(nft_token_id)
145+
.metadata(vec![b"Example NFT Metadata".to_vec()])
146+
.freeze_with(&client)?
147+
.sign(sender_key.clone())
148+
.execute(&client)
149+
.await?
150+
.get_receipt(&client)
151+
.await?
152+
.serials[0] as u64;
153+
154+
let nft_id = nft_token_id.nft(nft_serial);
155+
println!("Minted NFT: {nft_id}");
156+
157+
// Step 2: Demonstrate TransferTransaction API with hooks
158+
println!("\n=== TransferTransaction with Hooks API Demonstration ===");
159+
160+
// Create hook call objects
161+
println!("Creating hook call objects...");
162+
163+
// HBAR transfer with pre-tx allowance hook
164+
let hbar_hook = FungibleHookCall {
165+
hook_call: HookCall::new(Some(hook_id), {
166+
let mut evm_call = EvmHookCall::new(Some(vec![0x01, 0x02]));
167+
evm_call.set_gas_limit(20_000);
168+
Some(evm_call)
169+
}),
170+
hook_type: FungibleHookType::PreTxAllowanceHook,
171+
};
172+
173+
// NFT sender hook (pre-hook)
174+
let nft_sender_hook = NftHookCall {
175+
hook_call: HookCall::new(Some(hook_id), {
176+
let mut evm_call = EvmHookCall::new(Some(vec![0x03, 0x04]));
177+
evm_call.set_gas_limit(20_000);
178+
Some(evm_call)
179+
}),
180+
hook_type: NftHookType::PreHookSender,
181+
};
182+
183+
// NFT receiver hook (pre-hook)
184+
let nft_receiver_hook = NftHookCall {
185+
hook_call: HookCall::new(Some(hook_id), {
186+
let mut evm_call = EvmHookCall::new(Some(vec![0x05, 0x06]));
187+
evm_call.set_gas_limit(20_000);
188+
Some(evm_call)
189+
}),
190+
hook_type: NftHookType::PreHookReceiver,
191+
};
192+
193+
// Fungible token transfer with pre-post allowance hook
194+
let fungible_token_hook = FungibleHookCall {
195+
hook_call: HookCall::new(Some(hook_id), {
196+
let mut evm_call = EvmHookCall::new(Some(vec![0x07, 0x08]));
197+
evm_call.set_gas_limit(20_000);
198+
Some(evm_call)
199+
}),
200+
hook_type: FungibleHookType::PrePostTxAllowanceHook,
201+
};
202+
203+
// Build separate TransferTransactions with hooks
204+
println!("Building separate TransferTransactions with hooks...");
205+
206+
// Transaction 1: HBAR transfers with hook
207+
println!("\n1. Executing HBAR TransferTransaction with hook...");
208+
TransferTransaction::new()
209+
.add_hbar_transfer_with_hook(sender_account_id, Hbar::from_tinybars(-1), hbar_hook)
210+
.hbar_transfer(receiver_account_id, Hbar::from_tinybars(1))
211+
.freeze_with(&client)?
212+
.sign(sender_key.clone())
213+
.execute(&client)
214+
.await?
215+
.get_receipt(&client)
216+
.await?;
217+
println!(" ✓ HBAR transfer with pre-tx allowance hook completed");
218+
219+
// Transaction 2: NFT transfer with sender and receiver hooks
220+
println!("\n2. Executing NFT TransferTransaction with hooks...");
221+
TransferTransaction::new()
222+
.add_nft_transfer_with_hook(
223+
nft_id,
224+
sender_account_id,
225+
receiver_account_id,
226+
nft_sender_hook,
227+
nft_receiver_hook,
228+
)
229+
.freeze_with(&client)?
230+
.sign(sender_key.clone())
231+
.execute(&client)
232+
.await?
233+
.get_receipt(&client)
234+
.await?;
235+
println!(" ✓ NFT transfer with sender and receiver hooks completed");
236+
237+
// Transaction 3: Fungible token transfers with hook
238+
println!("\n3. Executing Fungible Token TransferTransaction with hook...");
239+
TransferTransaction::new()
240+
.add_token_transfer_with_hook(
241+
fungible_token_id,
242+
sender_account_id,
243+
-1_000,
244+
fungible_token_hook,
245+
)
246+
.token_transfer(fungible_token_id, receiver_account_id, 1_000)
247+
.freeze_with(&client)?
248+
.sign(sender_key.clone())
249+
.execute(&client)
250+
.await?
251+
.get_receipt(&client)
252+
.await?;
253+
println!(" ✓ Fungible token transfer with pre-post allowance hook completed");
254+
255+
println!("\nAll TransferTransactions executed successfully with the following hook calls:");
256+
println!(" - Transaction 1: HBAR transfer with pre-tx allowance hook");
257+
println!(" - Transaction 2: NFT transfer with sender and receiver hooks");
258+
println!(" - Transaction 3: Fungible token transfer with pre-post allowance hook");
259+
260+
println!("Transfer Transaction Hooks Example Complete!");
261+
262+
Ok(())
263+
}

src/hooks/fungible_hook_call.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
use hedera_proto::services;
22

33
use crate::hooks::{
4-
EvmHookCall,
54
FungibleHookType,
65
HookCall,
76
};
@@ -45,6 +44,7 @@ impl ToProtobuf for FungibleHookCall {
4544
#[cfg(test)]
4645
mod tests {
4746
use super::*;
47+
use crate::hooks::EvmHookCall;
4848

4949
#[test]
5050
fn test_fungible_hook_call_creation() {

0 commit comments

Comments
 (0)