Skip to content

Commit 8a60e01

Browse files
authored
Add ScVal conversion and SorobanArbitrary impls for MuxedAddress (#1782)
### What Add the missing `ScVal` conversion impls and `SorobanArbitrary` support for `MuxedAddress`. ### Why `#[contracttype]` generates test-only code paths that require field types to support conversion to and from `ScVal`, and to implement `SorobanArbitrary`. Without these impls, `MuxedAddress` cannot be used as a field in a `#[contracttype]` struct. Closes #1772 ### Known limitations None
1 parent f7bdf7e commit 8a60e01

File tree

5 files changed

+214
-2
lines changed

5 files changed

+214
-2
lines changed

soroban-sdk/src/muxed_address.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -353,6 +353,25 @@ impl MuxedAddress {
353353
}
354354
}
355355

356+
#[cfg(not(target_family = "wasm"))]
357+
impl From<&MuxedAddress> for ScVal {
358+
fn from(v: &MuxedAddress) -> Self {
359+
// This conversion occurs only in test utilities, and theoretically all
360+
// values should convert to an ScVal because the Env won't let the host
361+
// type to exist otherwise, unwrapping. Even if there are edge cases
362+
// that don't, this is a trade off for a better test developer
363+
// experience.
364+
ScVal::try_from_val(&v.env, &v.to_val()).unwrap()
365+
}
366+
}
367+
368+
#[cfg(not(target_family = "wasm"))]
369+
impl From<MuxedAddress> for ScVal {
370+
fn from(v: MuxedAddress) -> Self {
371+
(&v).into()
372+
}
373+
}
374+
356375
#[cfg(not(target_family = "wasm"))]
357376
impl TryFromVal<Env, ScVal> for MuxedAddress {
358377
type Error = ConversionError;

soroban-sdk/src/tests/muxed_address.rs

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use soroban_sdk_macros::{contract, contractimpl};
1+
use soroban_sdk_macros::{contract, contractimpl, contracttype};
22

33
use crate::testutils::{Address as _, MuxedAddress as _};
44
use crate::{self as soroban_sdk, Bytes, String};
@@ -7,6 +7,13 @@ use crate::{
77
Address, Env, MuxedAddress, TryFromVal,
88
};
99

10+
#[derive(Clone, Debug, Eq, PartialEq)]
11+
#[contracttype]
12+
pub struct Udt {
13+
pub address: MuxedAddress,
14+
pub amount: i128,
15+
}
16+
1017
#[contract]
1118
pub struct MuxedAddressContract;
1219

@@ -19,6 +26,10 @@ impl MuxedAddressContract {
1926
) -> (Option<u64>, Option<u64>) {
2027
(a.id(), b.id())
2128
}
29+
30+
pub fn echo_udt(_e: Env, udt: Udt) -> Udt {
31+
udt
32+
}
2233
}
2334

2435
#[test]
@@ -109,6 +120,32 @@ fn test_accept_muxed_address_argument_in_contract() {
109120
);
110121
}
111122

123+
#[test]
124+
fn test_contracttype_struct_with_address_backed_muxed_address() {
125+
let env = Env::default();
126+
let contract_id = env.register(MuxedAddressContract, ());
127+
let client = MuxedAddressContractClient::new(&env, &contract_id);
128+
129+
let udt = Udt {
130+
address: Address::generate(&env).into(),
131+
amount: 42,
132+
};
133+
assert_eq!(client.echo_udt(&udt), udt.clone());
134+
}
135+
136+
#[test]
137+
fn test_contracttype_struct_with_muxed_address() {
138+
let env = Env::default();
139+
let contract_id = env.register(MuxedAddressContract, ());
140+
let client = MuxedAddressContractClient::new(&env, &contract_id);
141+
142+
let udt = Udt {
143+
address: MuxedAddress::generate(&env),
144+
amount: 42,
145+
};
146+
assert_eq!(client.echo_udt(&udt), udt.clone());
147+
}
148+
112149
#[test]
113150
fn test_from_str_account() {
114151
let env = Env::default();

soroban-sdk/src/testutils/arbitrary.rs

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -328,7 +328,8 @@ mod objects {
328328
Fp, Fp2, Fr, G1Affine, G2Affine, FP2_SERIALIZED_SIZE, FP_SERIALIZED_SIZE,
329329
G1_SERIALIZED_SIZE, G2_SERIALIZED_SIZE,
330330
},
331-
Address, Bytes, BytesN, Duration, Map, String, Symbol, Timepoint, Val, Vec, I256, U256,
331+
Address, Bytes, BytesN, Duration, Map, MuxedAddress, String, Symbol, Timepoint, Val, Vec,
332+
I256, U256,
332333
};
333334

334335
use std::string::String as RustString;
@@ -653,6 +654,39 @@ mod objects {
653654

654655
//////////////////////////////////
655656

657+
#[derive(Arbitrary, Debug, Clone, Eq, PartialEq, Ord, PartialOrd)]
658+
pub enum ArbitraryMuxedAddress {
659+
Address(ArbitraryAddress),
660+
Muxed { ed25519: [u8; 32], id: u64 },
661+
}
662+
663+
impl SorobanArbitrary for MuxedAddress {
664+
type Prototype = ArbitraryMuxedAddress;
665+
}
666+
667+
impl TryFromVal<Env, ArbitraryMuxedAddress> for MuxedAddress {
668+
type Error = ConversionError;
669+
fn try_from_val(env: &Env, v: &ArbitraryMuxedAddress) -> Result<Self, Self::Error> {
670+
use crate::env::xdr::{MuxedEd25519Account, ScAddress, Uint256};
671+
672+
let sc_addr = match v {
673+
ArbitraryMuxedAddress::Address(v) => {
674+
let address = Address::try_from_val(env, v)?;
675+
return Ok(address.into());
676+
}
677+
ArbitraryMuxedAddress::Muxed { ed25519, id } => {
678+
ScVal::Address(ScAddress::MuxedAccount(MuxedEd25519Account {
679+
ed25519: Uint256(*ed25519),
680+
id: *id,
681+
}))
682+
}
683+
};
684+
Ok(sc_addr.into_val(env))
685+
}
686+
}
687+
688+
//////////////////////////////////
689+
656690
#[derive(Arbitrary, Debug, Clone, Eq, PartialEq, Ord, PartialOrd)]
657691
pub struct ArbitraryTimepoint {
658692
inner: u64,
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
{
2+
"generators": {
3+
"address": 2,
4+
"nonce": 0,
5+
"mux_id": 0
6+
},
7+
"auth": [
8+
[],
9+
[]
10+
],
11+
"ledger": {
12+
"protocol_version": 25,
13+
"sequence_number": 0,
14+
"timestamp": 0,
15+
"network_id": "0000000000000000000000000000000000000000000000000000000000000000",
16+
"base_reserve": 0,
17+
"min_persistent_entry_ttl": 4096,
18+
"min_temp_entry_ttl": 16,
19+
"max_entry_ttl": 6312000,
20+
"ledger_entries": [
21+
{
22+
"entry": {
23+
"last_modified_ledger_seq": 0,
24+
"data": {
25+
"contract_data": {
26+
"ext": "v0",
27+
"contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM",
28+
"key": "ledger_key_contract_instance",
29+
"durability": "persistent",
30+
"val": {
31+
"contract_instance": {
32+
"executable": {
33+
"wasm": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
34+
},
35+
"storage": null
36+
}
37+
}
38+
}
39+
},
40+
"ext": "v0"
41+
},
42+
"live_until": 4095
43+
},
44+
{
45+
"entry": {
46+
"last_modified_ledger_seq": 0,
47+
"data": {
48+
"contract_code": {
49+
"ext": "v0",
50+
"hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
51+
"code": ""
52+
}
53+
},
54+
"ext": "v0"
55+
},
56+
"live_until": 4095
57+
}
58+
]
59+
},
60+
"events": []
61+
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
{
2+
"generators": {
3+
"address": 2,
4+
"nonce": 0,
5+
"mux_id": 1
6+
},
7+
"auth": [
8+
[],
9+
[]
10+
],
11+
"ledger": {
12+
"protocol_version": 25,
13+
"sequence_number": 0,
14+
"timestamp": 0,
15+
"network_id": "0000000000000000000000000000000000000000000000000000000000000000",
16+
"base_reserve": 0,
17+
"min_persistent_entry_ttl": 4096,
18+
"min_temp_entry_ttl": 16,
19+
"max_entry_ttl": 6312000,
20+
"ledger_entries": [
21+
{
22+
"entry": {
23+
"last_modified_ledger_seq": 0,
24+
"data": {
25+
"contract_data": {
26+
"ext": "v0",
27+
"contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM",
28+
"key": "ledger_key_contract_instance",
29+
"durability": "persistent",
30+
"val": {
31+
"contract_instance": {
32+
"executable": {
33+
"wasm": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
34+
},
35+
"storage": null
36+
}
37+
}
38+
}
39+
},
40+
"ext": "v0"
41+
},
42+
"live_until": 4095
43+
},
44+
{
45+
"entry": {
46+
"last_modified_ledger_seq": 0,
47+
"data": {
48+
"contract_code": {
49+
"ext": "v0",
50+
"hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
51+
"code": ""
52+
}
53+
},
54+
"ext": "v0"
55+
},
56+
"live_until": 4095
57+
}
58+
]
59+
},
60+
"events": []
61+
}

0 commit comments

Comments
 (0)