templar_common/
upgrade.rs1use near_sdk::{
2 env,
3 json_types::{Base58CryptoHash, Base64VecU8},
4 near, Gas, NearToken, Promise,
5};
6
7pub const MIGRATE_METHOD: &str = "migrate";
9
10#[derive(Debug, Clone, PartialEq, Eq)]
22#[near(serializers = [json, borsh(use_discriminant = true)])]
23#[repr(u8)]
24pub enum UpgradeSource {
25 GlobalHash(Base58CryptoHash) = 1,
27 #[serde(untagged)]
29 Code(Base64VecU8) = 0,
30}
31
32#[derive(Debug, Clone, PartialEq, Eq)]
34#[near(serializers = [json])]
35pub enum UpgradeSummary {
36 CodeHash(Base58CryptoHash),
38 GlobalHash(Base58CryptoHash),
40}
41
42impl UpgradeSource {
43 pub fn is_empty_code(&self) -> bool {
45 matches!(self, UpgradeSource::Code(code) if code.0.is_empty())
46 }
47
48 pub fn summary(&self) -> UpgradeSummary {
50 match self {
51 UpgradeSource::Code(blob) => {
52 UpgradeSummary::CodeHash(env::sha256_array(&blob.0).into())
53 }
54 UpgradeSource::GlobalHash(hash) => UpgradeSummary::GlobalHash(*hash),
55 }
56 }
57
58 pub fn deploy_and_migrate(
62 self,
63 migrate_method: impl Into<String>,
64 migrate_args: Base64VecU8,
65 gas: Gas,
66 ) -> Promise {
67 let promise = Promise::new(env::current_account_id());
68 let deployed = match self {
69 UpgradeSource::Code(code) => promise.deploy_contract(code.0),
70 UpgradeSource::GlobalHash(hash) => promise.use_global_contract(hash),
71 };
72 deployed.function_call(
73 migrate_method.into(),
74 migrate_args.0,
75 NearToken::from_yoctonear(0),
76 gas,
77 )
78 }
79}
80
81#[cfg(test)]
82mod tests {
83 use super::*;
84 use near_sdk::serde_json::{self, json};
85
86 #[test]
87 fn code_is_untagged_bare_base64_in_json() {
88 let code = UpgradeSource::Code(Base64VecU8(vec![0xde, 0xad, 0xbe, 0xef]));
89 let value = serde_json::to_value(&code).unwrap();
90 assert_eq!(value, json!("3q2+7w=="));
92 assert_eq!(
93 serde_json::from_value::<UpgradeSource>(value).unwrap(),
94 code
95 );
96 }
97
98 #[test]
99 fn global_hash_stays_externally_tagged_in_json() {
100 let hash = UpgradeSource::GlobalHash(Base58CryptoHash::from([0u8; 32]));
101 let value = serde_json::to_value(&hash).unwrap();
102 assert_eq!(
103 value,
104 json!({ "GlobalHash": "11111111111111111111111111111111" })
105 );
106 assert_eq!(
107 serde_json::from_value::<UpgradeSource>(value).unwrap(),
108 hash
109 );
110 }
111
112 #[test]
113 fn both_variants_borsh_roundtrip() {
114 for source in [
115 UpgradeSource::Code(Base64VecU8(vec![1, 2, 3])),
116 UpgradeSource::GlobalHash(Base58CryptoHash::from([7u8; 32])),
117 ] {
118 let bytes = near_sdk::borsh::to_vec(&source).unwrap();
119 assert_eq!(
120 near_sdk::borsh::from_slice::<UpgradeSource>(&bytes).unwrap(),
121 source
122 );
123 }
124 }
125
126 #[test]
129 fn borsh_discriminants_are_stable() {
130 assert_eq!(
132 near_sdk::borsh::to_vec(&UpgradeSource::Code(Base64VecU8(vec![0xaa, 0xbb]))).unwrap(),
133 vec![0, 2, 0, 0, 0, 0xaa, 0xbb],
134 );
135 assert_eq!(
137 near_sdk::borsh::to_vec(&UpgradeSource::GlobalHash(Base58CryptoHash::from(
138 [0u8; 32]
139 )))
140 .unwrap(),
141 [&[1u8][..], &[0u8; 32][..]].concat(),
142 );
143 }
144}