@massalabs/multisig-contract 0.0.2-dev.20260414091108

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,307 @@
1
+ import {
2
+ bytesToF64,
3
+ bytesToU64,
4
+ bytesToString,
5
+ byteToBool,
6
+ stringToBytes,
7
+ u64ToBytes,
8
+ f64ToBytes,
9
+ u256ToBytes,
10
+ boolToByte,
11
+ Args,
12
+ } from '@massalabs/as-types';
13
+ import { Storage } from '@massalabs/massa-as-sdk';
14
+ import { Serializable } from '@massalabs/as-types';
15
+ import { u256 } from 'as-bignum/assembly/integer/u256';
16
+
17
+ export const _KEY_ELEMENT_SUFFIX = '::';
18
+
19
+ /**
20
+ * This class is one of several convenience collections built on top of the `Storage` class
21
+ * It implements a map -- a persistent unordered map.
22
+ *
23
+ * To create a map
24
+ *
25
+ * ```ts
26
+ * let map = new PersistentMap<string, string>("m") // choose a unique prefix per account
27
+ * ```
28
+ *
29
+ * To use the map
30
+ *
31
+ * ```ts
32
+ * map.set(key, value)
33
+ * map.get(key)
34
+ * ```
35
+ *
36
+ * IMPORTANT NOTES:
37
+ *
38
+ * (1) The Map doesn't store keys, so if you need to retrieve them, include keys in the values.
39
+ *
40
+ * (2) Since all data stored on the blockchain is kept in a single key-value store under the contract account,
41
+ * you must always use a *unique storage prefix* for different collections to avoid data collision.
42
+ *
43
+ * @typeParam K - The generic type parameter `K` can be any valid AssemblyScript type.
44
+ * @typeParam V - The generic type parameter `V` can be any valid AssemblyScript type.
45
+ *
46
+ * MISC:
47
+ *
48
+ * Original from Near (https://github.com/near/near-sdk-as/blob/master/sdk-core/assembly/collections/persistentMap.ts)
49
+ */
50
+ export class PersistentMap<K, V> {
51
+ private _elementPrefix: string;
52
+
53
+ /**
54
+ * Creates or restores a persistent map with a given storage prefix.
55
+ * Always use a unique storage prefix for different collections.
56
+ *
57
+ * Example
58
+ *
59
+ * ```ts
60
+ * let map = new PersistentMap<string, string>("m") // note the prefix must be unique (per MASSA account)
61
+ * ```
62
+ * @param prefix - A prefix to use for every key of this map.
63
+ */
64
+ constructor(prefix: string) {
65
+ this._elementPrefix = prefix + _KEY_ELEMENT_SUFFIX;
66
+ }
67
+
68
+ /**
69
+ * @param key - Search key.
70
+ * @returns An internal string key for a given key of type K.
71
+ */
72
+ private _key(key: K): StaticArray<u8> {
73
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
74
+ // @ts-ignore
75
+ return stringToBytes(this._elementPrefix + key.toString());
76
+ }
77
+
78
+ private _keySize(): StaticArray<u8> {
79
+ return stringToBytes('size::' + this._elementPrefix);
80
+ }
81
+
82
+ /**
83
+ * Checks whether the map contains a given key
84
+ *
85
+ * ```ts
86
+ * let map = new PersistentMap<string, string>("m")
87
+ *
88
+ * map.contains("hello") // false
89
+ * map.set("hello", "world")
90
+ * map.contains("hello") // true
91
+ * ```
92
+ *
93
+ * @param key - Key to check.
94
+ * @returns True if the given key present in the map.
95
+ */
96
+ contains(key: K): bool {
97
+ return Storage.has(this._key(key));
98
+ }
99
+
100
+ /**
101
+ * Returns the map size
102
+ *
103
+ * @example
104
+ * ```ts
105
+ * let map = new PersistentMap<string, string> ("m")
106
+ *
107
+ * map.size()
108
+ * ```
109
+ * @returns the map size
110
+ */
111
+ size(): u64 {
112
+ return Storage.has(this._keySize())
113
+ ? bytesToU64(Storage.get(this._keySize()))
114
+ : u64(0);
115
+ }
116
+
117
+ /**
118
+ * Removes the given key and related value from the map
119
+ *
120
+ * ```ts
121
+ * let map = new PersistentMap<string, string>("m")
122
+ *
123
+ * map.set("hello", "world")
124
+ * map.delete("hello")
125
+ * ```
126
+ *
127
+ * Removes value and the key from the map.
128
+ * @param key - Key to remove.
129
+ */
130
+ delete(key: K): void {
131
+ Storage.del(this._key(key));
132
+ this._decreaseSize();
133
+ }
134
+
135
+ /**
136
+ * Increases the internal map size counter
137
+ * @param key - Key to remove.
138
+ */
139
+ _increaseSize(key: K): void {
140
+ if (!this.contains(key)) {
141
+ Storage.set(this._keySize(), u64ToBytes(this.size() + 1));
142
+ }
143
+ }
144
+
145
+ /**
146
+ * Decreases the internal map size counter
147
+ */
148
+ _decreaseSize(): void {
149
+ if (this.size() > 0) {
150
+ Storage.set(this._keySize(), u64ToBytes(this.size() - 1));
151
+ }
152
+ }
153
+
154
+ /**
155
+ * Retrieves the related value for a given key, or uses the `defaultValue` if not key is found
156
+ *
157
+ * ```ts
158
+ * let map = new PersistentMap<string, string>("m")
159
+ *
160
+ * map.set("hello", "world")
161
+ * let found = map.get("hello")
162
+ * let notFound = map.get("goodbye", "cruel world")
163
+ *
164
+ * assert(found == "world")
165
+ * assert(notFound == "cruel world")
166
+ * ```
167
+ *
168
+ * @param key - Key of the element.
169
+ * @param defaultValue - The default value if the key is not present.
170
+ * @returns Value for the given key or the default value.
171
+ */
172
+ get(key: K, defaultValue: V): V {
173
+ if (!this.contains(key)) {
174
+ return defaultValue;
175
+ }
176
+ if (isString<V>()) {
177
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
178
+ // @ts-ignore
179
+ return bytesToString(Storage.get(this._key(key)));
180
+ } else if (isBoolean<V>()) {
181
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
182
+ // @ts-ignore
183
+ return byteToBool(Storage.get(this._key(key)));
184
+ } else if (isInteger<V>()) {
185
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
186
+ // @ts-ignore
187
+ return bytesToU64(Storage.get(this._key(key)));
188
+ } else if (isFloat<V>()) {
189
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
190
+ // @ts-ignore
191
+ return bytesToF64(Storage.get(this._key(key)));
192
+ } else if (idof<V>() == idof<StaticArray<u8>>()) {
193
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
194
+ // @ts-ignore
195
+ return Storage.get(this._key(key));
196
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
197
+ // @ts-ignore
198
+ } else if (defaultValue instanceof Serializable) {
199
+ return (
200
+ new Args(Storage.get(this._key(key)))
201
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
202
+ // @ts-ignore
203
+ .nextSerializable<V>()
204
+ .unwrap()
205
+ );
206
+ } else if (defaultValue instanceof u256) {
207
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
208
+ // @ts-ignore
209
+ return new Args(Storage.get(this._key(key))).nextU256().unwrap();
210
+ } else {
211
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
212
+ // @ts-ignore
213
+ return null;
214
+ }
215
+ }
216
+
217
+ /**
218
+ * Retrieves a related value for a given key or fails assertion with "key not found"
219
+ *
220
+ * ```ts
221
+ * let map = new PersistentMap<string, string>("m")
222
+ *
223
+ * map.set("hello", "world")
224
+ * let result = map.getSome("hello")
225
+ * // map.getSome("goodbye") // will throw with failed assertion
226
+ *
227
+ * assert(result == "world")
228
+ * ```
229
+ *
230
+ * @param key - Key of the element.
231
+ * @returns Value for the given key or the default value.
232
+ */
233
+ // eslint-disable-next-line
234
+ getSome(key: K, msg: string = 'key not found'): V {
235
+ assert(this.contains(key), msg);
236
+
237
+ if (isBoolean<V>()) {
238
+ const res = this.get(key, false as V);
239
+ return <V>res;
240
+ }
241
+
242
+ if (isInteger<V>()) {
243
+ const res = this.get(key, 1 as V);
244
+ return <V>res;
245
+ }
246
+ if (idof<V>() == idof<StaticArray<u8>>()) {
247
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
248
+ // @ts-ignore
249
+ const res = this.get(key, [] as V);
250
+ assert(res, 'bad result');
251
+ return <V>res;
252
+ }
253
+
254
+ const isSerializable = !isNullable<V>();
255
+ const res = this.get(key, isSerializable ? instantiate<V>() : (null as V));
256
+ assert(res, 'bad result');
257
+ return <V>res;
258
+
259
+ // if (!this.contains(key)) {
260
+ // return new Result(<V>null, "key not found");
261
+ // }
262
+ // const res = this.get(key, null);
263
+ // return new Result(<V>res);
264
+ }
265
+
266
+ /**
267
+ * ```ts
268
+ * let map = new PersistentMap<string, string>("m")
269
+ *
270
+ * map.set("hello", "world")
271
+ * ```
272
+ *
273
+ * Sets the new value for the given key.
274
+ * @param key - Key of the element.
275
+ * @param value - The new value of the element.
276
+ */
277
+ set(key: K, value: V): void {
278
+ // assert map size wont overflow
279
+ assert(this.size() < u64.MAX_VALUE, 'map size overflow');
280
+
281
+ this._increaseSize(key);
282
+
283
+ if (isString<V>()) {
284
+ Storage.set(this._key(key), stringToBytes(value as string));
285
+ } else if (isInteger<V>()) {
286
+ Storage.set(this._key(key), u64ToBytes(value as u64));
287
+ } else if (isFloat<V>()) {
288
+ Storage.set(this._key(key), f64ToBytes(value as f64));
289
+ } else if (isBoolean<V>()) {
290
+ Storage.set(this._key(key), boolToByte(value as bool));
291
+ } else if (idof<V>() == idof<StaticArray<u8>>()) {
292
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
293
+ // @ts-ignore
294
+ Storage.set(this._key(key), value);
295
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
296
+ // @ts-ignore
297
+ } else if (value instanceof Serializable) {
298
+ Storage.set(this._key(key), (value as Serializable).serialize());
299
+ } else if (value instanceof u256) {
300
+ Storage.set(this._key(key), u256ToBytes(value as u256));
301
+ } else {
302
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
303
+ // @ts-ignore
304
+ Storage.set(this._key(key), value.toString());
305
+ }
306
+ }
307
+ }
@@ -0,0 +1,73 @@
1
+ export class SafeMath {
2
+ /**
3
+ *
4
+ * @param a
5
+ * @param b
6
+ * @returns Returns the addition of two unsigned integers,
7
+ * reverting on overflow.
8
+ */
9
+ static add(a: u64, b: u64): u64 {
10
+ const c: u64 = a + b;
11
+ assert(c >= a, 'SafeMath: addition overflow');
12
+
13
+ return c;
14
+ }
15
+
16
+ /**
17
+ *
18
+ * @param a
19
+ * @param b
20
+ * @returns Returns the integer division of two unsigned integers. Reverts with custom message on
21
+ * division by zero. The result is rounded towards zero.
22
+ */
23
+ static sub(a: u64, b: u64): u64 {
24
+ assert(b <= a, 'SafeMath: substraction overflow');
25
+ const c: u64 = a - b;
26
+
27
+ return c;
28
+ }
29
+
30
+ /**
31
+ *
32
+ * @param a
33
+ * @param b
34
+ * @returns Returns the multiplication of two unsigned integers, reverting on
35
+ * overflow.
36
+ */
37
+ static mul(a: u64, b: u64): u64 {
38
+ if (a == 0) {
39
+ return 0;
40
+ }
41
+
42
+ const c = a * b;
43
+ assert(c / a == b, 'SafeMath: multiplication overflow');
44
+
45
+ return c;
46
+ }
47
+
48
+ /**
49
+ *
50
+ * @param a
51
+ * @param b
52
+ * @returns Returns the integer division of two unsigned integers. Reverts on
53
+ * division by zero. The result is rounded towards zero.
54
+ */
55
+ static div(a: u64, b: u64): u64 {
56
+ assert(b > 0, 'SafeMath: division by zero');
57
+ const c = a / b;
58
+
59
+ return c;
60
+ }
61
+
62
+ /**
63
+ *
64
+ * @param a
65
+ * @param b
66
+ * @returns Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
67
+ * Reverts with custom message when dividing by zero.
68
+ */
69
+ static mod(a: u64, b: u64): u64 {
70
+ assert(b != 0, 'SafeMath: modulo by zero');
71
+ return a % b;
72
+ }
73
+ }
@@ -0,0 +1,81 @@
1
+ import {
2
+ Context,
3
+ Storage,
4
+ caller,
5
+ createEvent,
6
+ generateEvent,
7
+ setBytecode,
8
+ } from '@massalabs/massa-as-sdk';
9
+ import { u64ToBytes, stringToBytes, bytesToU64 } from '@massalabs/as-types';
10
+ import { SafeMath } from './SafeMath';
11
+
12
+ /**
13
+ * @dev Make sure no other modules are using the same storage keys
14
+ */
15
+ const PERIOD: StaticArray<u8> = stringToBytes('upgradeable_period');
16
+ const TIMELOCK: StaticArray<u8> = stringToBytes('upgradeable_timelock');
17
+ const UPGRADE: StaticArray<u8> = stringToBytes('upgradeable_upgrade');
18
+
19
+ /**
20
+ * @dev Contract that allows to propose and execute upgrades
21
+ * The contract can be upgraded only after the locked period has passed
22
+ * Thoses function should only be callable by the owner
23
+ */
24
+ export class Upgradeable {
25
+ /**
26
+ * @dev Initializes the contract with the timelock period
27
+ */
28
+ static __Upgradeable_init(timelock: u64): void {
29
+ assert(!Storage.has(PERIOD), 'Upgradeable__AlreadyInitialized');
30
+
31
+ this.__Upgradeable_setUpgradeDelay(timelock);
32
+ }
33
+
34
+ /**
35
+ * @dev Update the upgrade delay
36
+ */
37
+ static __Upgradeable_setUpgradeDelay(timelock: u64): void {
38
+ Storage.set(PERIOD, u64ToBytes(timelock));
39
+ }
40
+
41
+ /**
42
+ * @dev Returns true if the contract is locked
43
+ */
44
+ static islocked(): bool {
45
+ return (
46
+ SafeMath.add(this.timelock(), bytesToU64(Storage.get(PERIOD))) <
47
+ Context.timestamp()
48
+ );
49
+ }
50
+
51
+ /**
52
+ * @dev Returns the timelock timestamp
53
+ */
54
+ static timelock(): u64 {
55
+ return bytesToU64(Storage.get(TIMELOCK));
56
+ }
57
+
58
+ /**
59
+ * @dev Proposes an upgrade, should be only callable by the owner
60
+ * @param newCode - The new contract code
61
+ */
62
+ static proposeUpgrade(newCode: StaticArray<u8>): void {
63
+ Storage.set(TIMELOCK, u64ToBytes(Context.timestamp()));
64
+ Storage.set(UPGRADE, newCode);
65
+ generateEvent(createEvent('UpgradeProposed', [caller().toString()]));
66
+ }
67
+
68
+ /**
69
+ * @dev Upgrades the contract, should be only callable by the owner
70
+ *
71
+ * reverts if the locked period has not passed
72
+ */
73
+ static upgrade(): void {
74
+ assert(!this.islocked(), 'Upgradeable__Timelock');
75
+ assert(Storage.has(UPGRADE), 'Upgradeable__NoUpgreadeProposed');
76
+ const newCode = Storage.get(UPGRADE);
77
+ Storage.del(UPGRADE);
78
+ setBytecode(newCode);
79
+ generateEvent(createEvent('Upgraded', [caller().toString()]));
80
+ }
81
+ }
@@ -0,0 +1,11 @@
1
+ import { stringToBytes } from '@massalabs/as-types';
2
+ import { PersistentMap } from '../libraries/PersistentMap';
3
+ import { Transaction } from '../structs/Transaction';
4
+
5
+ export const OWNERS = stringToBytes('owners');
6
+ export const DELAY = stringToBytes('delay');
7
+ export const REQUIRED = stringToBytes('required');
8
+ export const IS_OWNER = new PersistentMap<string, bool>('is_owner');
9
+ export const TRANSACTIONS = new PersistentMap<u64, Transaction>('transactions');
10
+ export const APPROVED = new PersistentMap<string, bool>('approved');
11
+ // @dev key is a combination of transaction id and owner address as string
@@ -0,0 +1,37 @@
1
+ import { Serializable, Args, Result } from '@massalabs/as-types';
2
+ import { Address } from '@massalabs/massa-as-sdk';
3
+
4
+ export class Transaction implements Serializable {
5
+ constructor(
6
+ public to: Address = new Address(''),
7
+ public method: string = '',
8
+ public value: u64 = 0,
9
+ public data: StaticArray<u8> = [],
10
+ public timestamp: u64 = 0,
11
+ public executed: bool = false,
12
+ ) {}
13
+
14
+ serialize(): StaticArray<u8> {
15
+ return new Args()
16
+ .add(this.to)
17
+ .add(this.method)
18
+ .add(this.value)
19
+ .add(this.data)
20
+ .add(this.timestamp)
21
+ .add(this.executed)
22
+ .serialize();
23
+ }
24
+
25
+ deserialize(data: StaticArray<u8>, offset: i32): Result<i32> {
26
+ const args = new Args(data, offset);
27
+ this.to = new Address(args.nextString().expect('Error deserializing to'));
28
+ this.method = args.nextString().expect('Error deserializing method');
29
+ this.value = args.nextU64().expect('Error deserializing value');
30
+ this.data = args.nextBytes().expect('Error deserializing data');
31
+ const timestamp = args.nextU64();
32
+ this.timestamp = timestamp.isOk() ? timestamp.unwrap() : 0;
33
+ const executed = args.nextBool();
34
+ this.executed = executed.isOk() ? executed.unwrap() : false;
35
+ return new Result(args.offset);
36
+ }
37
+ }
@@ -0,0 +1,14 @@
1
+ {
2
+ "extends": "../node_modules/assemblyscript/std/assembly.json",
3
+ "include": [
4
+ "**/*.ts"
5
+ ],
6
+ "typedocOptions": {
7
+ "exclude": "assembly/**/__tests__",
8
+ "excludePrivate": true,
9
+ "excludeProtected": true,
10
+ "excludeExternals": true,
11
+ "includeVersion": true,
12
+ "skipErrorChecking": true
13
+ }
14
+ }
package/index.ts ADDED
@@ -0,0 +1 @@
1
+ export * from './assembly/interfaces/IMultisig';
package/package.json ADDED
@@ -0,0 +1,57 @@
1
+ {
2
+ "name": "@massalabs/multisig-contract",
3
+ "version": "0.0.2-dev.20260414091108",
4
+ "description": "",
5
+ "scripts": {
6
+ "test": "asp --summary",
7
+ "build": "npx massa-as-compile",
8
+ "deploy": "tsx src/deploy.ts",
9
+ "approve:proposal": "tsx src/approve-proposal.ts",
10
+ "execute:proposal": "tsx src/execute-proposal.ts",
11
+ "get:multisig-parameters": "tsx src/get-multisig-parameters.ts",
12
+ "list:proposals": "tsx src/list-proposals.ts",
13
+ "propose:add-member": "tsx src/propose-add-member.ts",
14
+ "prettier": "prettier assembly//**/*.ts --check",
15
+ "prettier:fix": "prettier assembly//**/*.ts --write",
16
+ "lint": "eslint .",
17
+ "lint:fix": "eslint . --fix",
18
+ "fmt:check": "npm run prettier && npm run lint",
19
+ "fmt": "npm run prettier:fix && npm run lint:fix",
20
+ "publish:dev": "bash ./scripts/publish-dev.sh",
21
+ "publish:latest": "npm publish --access public",
22
+ "prepublishOnly": "npm run build"
23
+ },
24
+ "keywords": [],
25
+ "author": "",
26
+ "license": "ISC",
27
+ "devDependencies": {
28
+ "@as-pect/cli": "^8.0.1",
29
+ "@assemblyscript/loader": "^0.27.22",
30
+ "@massalabs/as-transformer": "^0.3.2",
31
+ "@massalabs/as-types": "^2.1.0",
32
+ "@massalabs/eslint-config": "^0.0.10",
33
+ "@massalabs/massa-as-sdk": "^3.0.2",
34
+ "@massalabs/massa-sc-compiler": "^0.1.0",
35
+ "@massalabs/massa-web3": "^5.3.0",
36
+ "@massalabs/prettier-config-as": "^0.0.2",
37
+ "@massalabs/sc-standards": "^0.1.1",
38
+ "@types/node": "^20.10.3",
39
+ "as-bignum": "github:massalabs/as-bignum#0105eb596b2fa707c00712e811a2efdfcb8a9848",
40
+ "assemblyscript": "^0.27.22",
41
+ "assemblyscript-prettier": "^3.0.4",
42
+ "dotenv": "^16.3.1",
43
+ "prettier": "^3.1.0",
44
+ "tslib": "^2.6.2",
45
+ "tsx": "^4.11.0",
46
+ "typescript": "^5.3.2"
47
+ },
48
+ "type": "module",
49
+ "prettier": "@massalabs/prettier-config-as",
50
+ "files": [
51
+ "index.ts",
52
+ "assembly"
53
+ ],
54
+ "engines": {
55
+ "node": ">=16"
56
+ }
57
+ }