Skip to content

Commit bf43368

Browse files
Add contractevent types for tokens and stellar asset contract (#1489)
### What Add `contractevent` types for tokens and stellar asset contract. Remove event publishing helpers. ### Why With the addition of the `contractevent` macro in #1473 it makes sense to use the macro to update the specs generated for the token and the stellar asset contract such that their generated specs include the events. The `transfer` event added is represented with two event types: - `Transfer`, which is the `single-value` data with an `amount`. - `TransferMuxed`, which is the `map` data with a non-optional `to_muxed_id`. Two types are used because of the differing data format type, and the contract event schema does not support multiple data format types to be represented in a single type, to avoid complexity. The reason that a non-optional `to_muxed_id` is used is because without https://github.com/orgs/stellar/discussions/1750 the option none would be stored as a void. In the future if that change occurs it may make sense to update `TransferMuxed` to `Transfer2`, or similar, where-by it represents some larger set of parameters that may grow over time. The existing event publishing helpers are removed, rather than deprecated, because the helpers build versions of the events that will no longer be used by the stellar asset contract after CAP-67, versions that include an admin topic. If the helpers remain, folks may continue to call them and emit the topic. If the functions were updated, it would be a breaking change anyway. It is more obvious to make the break by removing them and encouraging use of the new events. Because the new event types carry parameters as fields, it is very explicit that the admin topic is gone. Close #1097 Close #1488 ### Merging This change is intended to be merged to `main` after #1473.
1 parent dff9656 commit bf43368

File tree

14 files changed

+1143
-76
lines changed

14 files changed

+1143
-76
lines changed

soroban-sdk/src/tests.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,4 +42,6 @@ mod proptest_scval_cmp;
4242
mod proptest_val_cmp;
4343
mod storage_testutils;
4444
mod token_client;
45+
mod token_events;
46+
mod token_events_stellar_asset;
4547
mod token_spec;
Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
use crate::{self as soroban_sdk, IntoVal};
2+
3+
use soroban_sdk::{
4+
contract, symbol_short,
5+
testutils::{Address as _, Events as _},
6+
token::{Approve, Burn, Clawback, Mint, Transfer, TransferMuxed},
7+
vec, Address, Env, Event, Map, Symbol, Val,
8+
};
9+
10+
#[contract]
11+
struct Contract;
12+
13+
#[test]
14+
fn test_approve() {
15+
let env = Env::default();
16+
let id = env.register(Contract, ());
17+
let event = Approve {
18+
from: Address::generate(&env),
19+
spender: Address::generate(&env),
20+
amount: 123,
21+
expiration_ledger: 45,
22+
};
23+
env.as_contract(&id, || event.publish(&env));
24+
assert_eq!(
25+
env.events().all(),
26+
vec![
27+
&env,
28+
(
29+
id.clone(),
30+
(
31+
symbol_short!("approve"),
32+
event.from.clone(),
33+
event.spender.clone(),
34+
)
35+
.into_val(&env),
36+
(123i128, 45u32,).into_val(&env),
37+
),
38+
]
39+
);
40+
}
41+
42+
#[test]
43+
fn test_transfer() {
44+
let env = Env::default();
45+
let id = env.register(Contract, ());
46+
let event = Transfer {
47+
from: Address::generate(&env),
48+
to: Address::generate(&env),
49+
amount: 123,
50+
};
51+
env.as_contract(&id, || event.publish(&env));
52+
assert_eq!(
53+
env.events().all(),
54+
vec![
55+
&env,
56+
(
57+
id.clone(),
58+
(
59+
symbol_short!("transfer"),
60+
event.from.clone(),
61+
event.to.clone(),
62+
)
63+
.into_val(&env),
64+
123i128.into_val(&env),
65+
),
66+
]
67+
);
68+
}
69+
70+
#[test]
71+
fn test_transfer_muxed() {
72+
let env = Env::default();
73+
let id = env.register(Contract, ());
74+
let event = TransferMuxed {
75+
from: Address::generate(&env),
76+
to: Address::generate(&env),
77+
to_muxed_id: 45,
78+
amount: 123,
79+
};
80+
env.as_contract(&id, || event.publish(&env));
81+
assert_eq!(
82+
env.events().all(),
83+
vec![
84+
&env,
85+
(
86+
id.clone(),
87+
(
88+
symbol_short!("transfer"),
89+
event.from.clone(),
90+
event.to.clone(),
91+
)
92+
.into_val(&env),
93+
Map::<Symbol, Val>::from_array(
94+
&env,
95+
[
96+
(Symbol::new(&env, "to_muxed_id"), 45u32.into_val(&env)),
97+
(Symbol::new(&env, "amount"), 123i128.into_val(&env)),
98+
],
99+
)
100+
.into(),
101+
),
102+
]
103+
);
104+
}
105+
106+
#[test]
107+
fn test_burn() {
108+
let env = Env::default();
109+
let id = env.register(Contract, ());
110+
let event = Burn {
111+
from: Address::generate(&env),
112+
amount: 123,
113+
};
114+
env.as_contract(&id, || event.publish(&env));
115+
assert_eq!(
116+
env.events().all(),
117+
vec![
118+
&env,
119+
(
120+
id.clone(),
121+
(symbol_short!("burn"), event.from.clone(),).into_val(&env),
122+
123i128.into_val(&env),
123+
),
124+
]
125+
);
126+
}
127+
128+
#[test]
129+
fn test_mint() {
130+
let env = Env::default();
131+
let id = env.register(Contract, ());
132+
let event = Mint {
133+
to: Address::generate(&env),
134+
amount: 123,
135+
};
136+
env.as_contract(&id, || event.publish(&env));
137+
assert_eq!(
138+
env.events().all(),
139+
vec![
140+
&env,
141+
(
142+
id.clone(),
143+
(symbol_short!("mint"), event.to.clone(),).into_val(&env),
144+
123i128.into_val(&env),
145+
),
146+
]
147+
);
148+
}
149+
150+
#[test]
151+
fn test_clawback() {
152+
let env = Env::default();
153+
let id = env.register(Contract, ());
154+
let event = Clawback {
155+
from: Address::generate(&env),
156+
amount: 123,
157+
};
158+
env.as_contract(&id, || event.publish(&env));
159+
assert_eq!(
160+
env.events().all(),
161+
vec![
162+
&env,
163+
(
164+
id.clone(),
165+
(symbol_short!("clawback"), event.from.clone(),).into_val(&env),
166+
123i128.into_val(&env),
167+
),
168+
]
169+
);
170+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
use crate::{self as soroban_sdk, IntoVal};
2+
3+
use soroban_sdk::{
4+
contract, symbol_short,
5+
testutils::{Address as _, Events as _},
6+
token::{SetAdmin, SetAuthorized},
7+
vec, Address, Env, Event, Symbol,
8+
};
9+
10+
#[contract]
11+
struct Contract;
12+
13+
#[test]
14+
fn test_set_admin() {
15+
let env = Env::default();
16+
let id = env.register(Contract, ());
17+
let event = SetAdmin {
18+
new_admin: Address::generate(&env),
19+
};
20+
env.as_contract(&id, || event.publish(&env));
21+
assert_eq!(
22+
env.events().all(),
23+
vec![
24+
&env,
25+
(
26+
id.clone(),
27+
(symbol_short!("set_admin"),).into_val(&env),
28+
event.new_admin.into_val(&env),
29+
),
30+
]
31+
);
32+
}
33+
34+
#[test]
35+
fn test_set_authorized() {
36+
let env = Env::default();
37+
let id = env.register(Contract, ());
38+
let event = SetAuthorized {
39+
id: Address::generate(&env),
40+
authorize: true,
41+
};
42+
env.as_contract(&id, || event.publish(&env));
43+
assert_eq!(
44+
env.events().all(),
45+
vec![
46+
&env,
47+
(
48+
id.clone(),
49+
(Symbol::new(&env, "set_authorized"), event.id.clone(),).into_val(&env),
50+
true.into_val(&env),
51+
),
52+
]
53+
);
54+
}

soroban-sdk/src/token.rs

Lines changed: 79 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
//! Use [`TokenClient`] for calling token contracts such as the Stellar Asset
88
//! Contract.
99
10-
use crate::{contractclient, contractspecfn, Address, Env, String};
10+
use crate::{contractclient, contractevent, contractspecfn, Address, Env, String};
1111

1212
// The interface below was copied from
1313
// https://github.com/stellar/rs-soroban-env/blob/main/soroban-env-host/src/native_contract/token/contract.rs
@@ -230,6 +230,56 @@ pub trait TokenInterface {
230230
fn symbol(env: Env) -> String;
231231
}
232232

233+
#[contractevent(crate_path = "crate", topics = ["approve"], data_format = "vec", export = false)]
234+
pub struct Approve {
235+
#[topic]
236+
pub from: Address,
237+
#[topic]
238+
pub spender: Address,
239+
pub amount: i128,
240+
pub expiration_ledger: u32,
241+
}
242+
243+
#[contractevent(crate_path = "crate", topics = ["transfer"], data_format = "single-value", export = false)]
244+
pub struct Transfer {
245+
#[topic]
246+
pub from: Address,
247+
#[topic]
248+
pub to: Address,
249+
pub amount: i128,
250+
}
251+
252+
#[contractevent(crate_path = "crate", topics = ["transfer"], data_format = "map", export = false)]
253+
pub struct TransferMuxed {
254+
#[topic]
255+
pub from: Address,
256+
#[topic]
257+
pub to: Address,
258+
pub to_muxed_id: u32,
259+
pub amount: i128,
260+
}
261+
262+
#[contractevent(crate_path = "crate", topics = ["burn"], data_format = "single-value", export = false)]
263+
pub struct Burn {
264+
#[topic]
265+
pub from: Address,
266+
pub amount: i128,
267+
}
268+
269+
#[contractevent(crate_path = "crate", topics = ["mint"], data_format = "single-value", export = false)]
270+
pub struct Mint {
271+
#[topic]
272+
pub to: Address,
273+
pub amount: i128,
274+
}
275+
276+
#[contractevent(crate_path = "crate", topics = ["clawback"], data_format = "single-value", export = false)]
277+
pub struct Clawback {
278+
#[topic]
279+
pub from: Address,
280+
pub amount: i128,
281+
}
282+
233283
/// Spec contains the contract spec of Token contracts.
234284
#[doc(hidden)]
235285
pub struct TokenSpec;
@@ -245,9 +295,15 @@ pub(crate) const TOKEN_SPEC_XDR_INPUT: &[&[u8]] = &[
245295
&TokenSpec::spec_xdr_symbol(),
246296
&TokenSpec::spec_xdr_transfer(),
247297
&TokenSpec::spec_xdr_transfer_from(),
298+
&Approve::spec_xdr(),
299+
&Transfer::spec_xdr(),
300+
&TransferMuxed::spec_xdr(),
301+
&Burn::spec_xdr(),
302+
&Mint::spec_xdr(),
303+
&Clawback::spec_xdr(),
248304
];
249305

250-
pub(crate) const TOKEN_SPEC_XDR_LEN: usize = 4716;
306+
pub(crate) const TOKEN_SPEC_XDR_LEN: usize = 5388;
251307

252308
impl TokenSpec {
253309
/// Returns the XDR spec for the Token contract.
@@ -499,6 +555,18 @@ pub trait StellarAssetInterface {
499555
fn clawback(env: Env, from: Address, amount: i128);
500556
}
501557

558+
#[contractevent(crate_path = "crate", topics = ["set_admin"], data_format = "single-value", export = false)]
559+
pub struct SetAdmin {
560+
pub new_admin: Address,
561+
}
562+
563+
#[contractevent(crate_path = "crate", topics = ["set_authorized"], data_format = "single-value", export = false)]
564+
pub struct SetAuthorized {
565+
#[topic]
566+
pub id: Address,
567+
pub authorize: bool,
568+
}
569+
502570
/// Spec contains the contract spec of the Stellar Asset Contract.
503571
///
504572
/// The Stellar Asset Contract is a superset of the Token Contract.
@@ -522,9 +590,17 @@ pub(crate) const STELLAR_ASSET_SPEC_XDR_INPUT: &[&[u8]] = &[
522590
&StellarAssetSpec::spec_xdr_symbol(),
523591
&StellarAssetSpec::spec_xdr_transfer(),
524592
&StellarAssetSpec::spec_xdr_transfer_from(),
593+
&Approve::spec_xdr(),
594+
&Transfer::spec_xdr(),
595+
&TransferMuxed::spec_xdr(),
596+
&Burn::spec_xdr(),
597+
&Mint::spec_xdr(),
598+
&Clawback::spec_xdr(),
599+
&SetAdmin::spec_xdr(),
600+
&SetAuthorized::spec_xdr(),
525601
];
526602

527-
pub(crate) const STELLAR_ASSET_SPEC_XDR_LEN: usize = 6456;
603+
pub(crate) const STELLAR_ASSET_SPEC_XDR_LEN: usize = 7320;
528604

529605
impl StellarAssetSpec {
530606
/// Returns the XDR spec for the Token contract.

0 commit comments

Comments
 (0)