diff --git a/packages/ckb-sdk-rpc/types/rpc/index.d.ts b/packages/ckb-sdk-rpc/types/rpc/index.d.ts
index ec541071..12272f9a 100644
--- a/packages/ckb-sdk-rpc/types/rpc/index.d.ts
+++ b/packages/ckb-sdk-rpc/types/rpc/index.d.ts
@@ -33,7 +33,7 @@ declare module RPC {
Committed = 'committed',
}
- export type ScriptHashType = 'data' | 'type'
+ export type ScriptHashType = CKBComponents.ScriptHashType
export type DepType = 'code' | 'dep_group'
diff --git a/packages/ckb-sdk-utils/README.md b/packages/ckb-sdk-utils/README.md
index 21fffeee..1f467a5c 100644
--- a/packages/ckb-sdk-utils/README.md
+++ b/packages/ckb-sdk-utils/README.md
@@ -13,9 +13,10 @@ See [Full Doc](https://github.com/nervosnetwork/ckb-sdk-js/blob/develop/README.m
- `utils.privateKeyToAddress`: get address from private key
- `utils.pubkeyToAddress`: get address from public key
- `utils.bech32Address`: args to short/full version address
- - `utils.fullPayloadToAddress`: script to full version address
+ - `utils.fullPayloadToAddress`: script to full version address of obselete version, **deprecated and use `utils.scriptToAddress` instead**
- `utils.parseAddress`: get address payload
- `utils.addressToScript`: get lock script from address
+ - `utils.scriptToAddress`: get full address of new version from script
- [Utils](#utils)
@@ -148,6 +149,19 @@ utils.addressToScript('ckb1qsvf96jqmq4483ncl7yrzfzshwchu9jd0glq4yy5r2jcsw04d7xly
// }
```
+```js
+/**
+ * @description generate full address of new version from script, the address conforms to format type 0x00
+ * @tutorial https://github.com/nervosnetwork/rfcs/pull/239/
+ */
+utils.scriptToAddress({
+ "codeHash": "0x9bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce8",
+ "hashType": "type",
+ "args":"0xb39bbc0b3673c7d36450bc14cfcdad2d559c6c64"
+})
+// ckb1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xwsqdnnw7qkdnnclfkg59uzn8umtfd2kwxceqxwquc4
+```
+
### Utils
```plain
diff --git a/packages/ckb-sdk-utils/__tests__/address/fixtures.json b/packages/ckb-sdk-utils/__tests__/address/fixtures.json
index 6cd2b6b5..53fbc3fd 100644
--- a/packages/ckb-sdk-utils/__tests__/address/fixtures.json
+++ b/packages/ckb-sdk-utils/__tests__/address/fixtures.json
@@ -3,6 +3,22 @@
"basic": {
"params": ["0x36c329ed630d6ce750712a477543672adab57f4c"],
"expected": [1, 0, 54, 195, 41, 237, 99, 13, 108, 231, 80, 113, 42, 71, 117, 67, 103, 42, 218, 181, 127, 76]
+ },
+ "full address of new version specifies hash_type = type": {
+ "params": ["0xb39bbc0b3673c7d36450bc14cfcdad2d559c6c64", "0x00", "0xa656f172b6b45c245307aeb5a7a37a176f002f6f22e92582c58bf7ba362e4176", "data1"],
+ "expected": [0, 166, 86, 241, 114, 182, 180, 92, 36, 83, 7, 174, 181, 167, 163, 122, 23, 111, 0, 47, 111, 34, 233, 37, 130, 197, 139, 247, 186, 54, 46, 65, 118, 2, 179, 155, 188, 11, 54, 115, 199, 211, 100, 80, 188, 20, 207, 205, 173, 45, 85, 156, 108, 100]
+ },
+ "should throw an error when its a full version address identifies the hash_type but code hash doesn't start with 0x": {
+ "params": ["0x36c329ed630d6ce750712a477543672adab57f4c", "0x00", "3419a1c09eb2567f6552ee7a8ecffd64155cffe0f1796e6e61ec088d740c1356"],
+ "exception": "'3419a1c09eb2567f6552ee7a8ecffd64155cffe0f1796e6e61ec088d740c1356' is not a valid code hash"
+ },
+ "should throw an error when its a full version address identifies the hash_type but code hash has invalid length": {
+ "params": ["0x36c329ed630d6ce750712a477543672adab57f4c", "0x00", "0x3419a1c09eb2567f6552ee7a8ecffd64155cffe0f1796e6e61ec088d740c135"],
+ "exception": "'0x3419a1c09eb2567f6552ee7a8ecffd64155cffe0f1796e6e61ec088d740c135' is not a valid code hash"
+ },
+ "should throw an error when its a full version address identifies the hash_type but hash_type is missing": {
+ "params": ["0xb39bbc0b3673c7d36450bc14cfcdad2d559c6c64", "0x00", "0x9bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce8"],
+ "exception": "hashType is required"
}
},
"fullPayloadToAddress": {
@@ -106,35 +122,47 @@
},
"data hash type full version address": {
"params": ["ckb1q2da0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xw3vumhs9nvu786dj9p0q5elx66t24n3kxgdwd2q8"],
- "expected": [ 2, 155, 215, 224, 111, 62, 207, 75, 224, 242, 252, 210, 24, 139, 35, 241, 185, 252, 200, 142, 93, 75, 101, 168, 99, 123, 23, 114, 59, 189, 163, 204, 232, 179, 155, 188, 11, 54, 115, 199, 211, 100, 80, 188, 20, 207, 205, 173, 45, 85, 156, 108, 100 ]
+ "expected": [2, 155, 215, 224, 111, 62, 207, 75, 224, 242, 252, 210, 24, 139, 35, 241, 185, 252, 200, 142, 93, 75, 101, 168, 99, 123, 23, 114, 59, 189, 163, 204, 232, 179, 155, 188, 11, 54, 115, 199, 211, 100, 80, 188, 20, 207, 205, 173, 45, 85, 156, 108, 100]
},
"type hash type full version address": {
"params": ["ckb1qsvf96jqmq4483ncl7yrzfzshwchu9jd0glq4yy5r2jcsw04d7xlydkr98kkxrtvuag8z2j8w4pkw2k6k4l5czfy37k"],
- "expected": [ 4, 24, 146, 234, 64, 216, 43, 83, 198, 120, 255, 136, 49, 36, 80, 187, 177, 126, 22, 77, 122, 62, 10, 144, 148, 26, 165, 136, 57, 245, 111, 141, 242, 54, 195, 41, 237, 99, 13, 108, 231, 80, 113, 42, 71, 117, 67, 103, 42, 218, 181, 127, 76 ]
+ "expected": [4, 24, 146, 234, 64, 216, 43, 83, 198, 120, 255, 136, 49, 36, 80, 187, 177, 126, 22, 77, 122, 62, 10, 144, 148, 26, 165, 136, 57, 245, 111, 141, 242, 54, 195, 41, 237, 99, 13, 108, 231, 80, 113, 42, 71, 117, 67, 103, 42, 218, 181, 127, 76]
+ },
+ "full version address identifies the hash_type": {
+ "params": ["ckt1qq6pngwqn6e9vlm92th84rk0l4jp2h8lurchjmnwv8kq3rt5psf4vqgqza8m903wt5xp5wuxjnurydg2x0qksh280gxqzqgutrqyp"],
+ "expected": [0, 52, 25, 161, 192, 158, 178, 86, 127, 101, 82, 238, 122, 142, 207, 253, 100, 21, 92, 255, 224, 241, 121, 110, 110, 97, 236, 8, 141, 116, 12, 19, 86, 1, 0, 23, 79, 178, 190, 46, 93, 12, 26, 59, 134, 148, 248, 50, 53, 10, 51, 193, 104, 93, 71, 122, 12, 1, 1]
},
"should throw an error when short version address has invalid payload size": {
"params": ["ckt1qyqrdsefa43s6m882pcj53m4gdnj4k440axqqm65l9j"],
- "exception": "ckt1qyqrdsefa43s6m882pcj53m4gdnj4k440axqqm65l9j is not a valid short version address"
+ "exception": "'ckt1qyqrdsefa43s6m882pcj53m4gdnj4k440axqqm65l9j' is not a valid short version address"
},
"should throw an error when anyone can pay address has invalid payload size": {
"params": ["ckt1qyprdsefa43s6m882pcj53m4gdnj4k440axqqfmyd9c"],
- "exception": "ckt1qyprdsefa43s6m882pcj53m4gdnj4k440axqqfmyd9c is not a valid short version address"
+ "exception": "'ckt1qyprdsefa43s6m882pcj53m4gdnj4k440axqqfmyd9c' is not a valid short version address"
},
"should throw an error when address type is invalid": {
"params": ["ckt1qwn9dutjk669cfznq7httfar0gtk7qp0du3wjfvzck9l0w3k9eqhvdkr98kkxrtvuag8z2j8w4pkw2k6k4l5ctv25r2"],
- "exception": "ckt1qwn9dutjk669cfznq7httfar0gtk7qp0du3wjfvzck9l0w3k9eqhvdkr98kkxrtvuag8z2j8w4pkw2k6k4l5ctv25r2 is not a valid address"
+ "exception": "'ckt1qwn9dutjk669cfznq7httfar0gtk7qp0du3wjfvzck9l0w3k9eqhvdkr98kkxrtvuag8z2j8w4pkw2k6k4l5ctv25r2' is not a valid address"
},
"should throw an error when hash type is invalid": {
"params": ["ckt1qwn9dutjk669cfznq7httfar0gtk7qp0du3wjfvzck9l0w3k9eqhvdkr98kkxrtvuag8z2j8w4pkw2k6k4l5ctv25r2"],
- "exception": "ckt1qwn9dutjk669cfznq7httfar0gtk7qp0du3wjfvzck9l0w3k9eqhvdkr98kkxrtvuag8z2j8w4pkw2k6k4l5ctv25r2 is not a valid address"
+ "exception": "'ckt1qwn9dutjk669cfznq7httfar0gtk7qp0du3wjfvzck9l0w3k9eqhvdkr98kkxrtvuag8z2j8w4pkw2k6k4l5ctv25r2' is not a valid address"
},
"should throw an error when code hash index is invalid": {
"params": ["ckt1qyzndsefa43s6m882pcj53m4gdnj4k440axqcth0hp"],
- "exception": "ckt1qyzndsefa43s6m882pcj53m4gdnj4k440axqcth0hp is not a valid short version address"
+ "exception": "'ckt1qyzndsefa43s6m882pcj53m4gdnj4k440axqcth0hp' is not a valid short version address"
},
"should throw an error when full version address has invalid size": {
"params": ["ckb1qsqcjt4ypkpt20r83lugxyj9pwa30cty6737p2gfgx493qul2cgvrxhw"],
- "exception": "ckb1qsqcjt4ypkpt20r83lugxyj9pwa30cty6737p2gfgx493qul2cgvrxhw is not a valid full version address"
+ "exception": "'ckb1qsqcjt4ypkpt20r83lugxyj9pwa30cty6737p2gfgx493qul2cgvrxhw' is not a valid full version address"
+ },
+ "should throw an error when full version address identifies the hash_type has invalid code hash": {
+ "params": ["ckb1qqv6rsy7kft87e2jaeaganlavs24ellq79ukumnpasyg6aqvzdtqzukxep"],
+ "exception": "'ckb1qqv6rsy7kft87e2jaeaganlavs24ellq79ukumnpasyg6aqvzdtqzukxep' is not a valid address"
+ },
+ "should throw an error when full version address identifies the hash_type has invalid hash type": {
+ "params": ["ckb1qq6pngwqn6e9vlm92th84rk0l4jp2h8lurchjmnwv8kq3rt5psf4vqcqza8m903wt5xp5wuxjnurydg2x0qksh280gxqzqgaxsc2r"],
+ "exception": "'ckb1qq6pngwqn6e9vlm92th84rk0l4jp2h8lurchjmnwv8kq3rt5psf4vqcqza8m903wt5xp5wuxjnurydg2x0qksh280gxqzqgaxsc2r' is not a valid address"
}
},
"addressToScript": {
@@ -185,6 +213,106 @@
"hashType": "type",
"args": "0x36c329ed630d6ce750712a477543672adab57f4c"
}
+ },
+ "full version address identifies hash_type = type": {
+ "params": ["ckb1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xwsqdnnw7qkdnnclfkg59uzn8umtfd2kwxceqxwquc4"],
+ "expected": {
+ "codeHash": "0x9bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce8",
+ "hashType": "type",
+ "args": "0xb39bbc0b3673c7d36450bc14cfcdad2d559c6c64"
+ }
+ },
+ "full version address identifies hash_type = data1": {
+ "params": ["ckt1qzn9dutjk669cfznq7httfar0gtk7qp0du3wjfvzck9l0w3k9eqhvq4nnw7qkdnnclfkg59uzn8umtfd2kwxceq225jvu"],
+ "expected": {
+ "codeHash": "0xa656f172b6b45c245307aeb5a7a37a176f002f6f22e92582c58bf7ba362e4176",
+ "hashType": "data1",
+ "args": "0xb39bbc0b3673c7d36450bc14cfcdad2d559c6c64"
+ }
+ }
+ },
+ "scriptToAddress": {
+ "full version mainnet address identifies hash_type = type": {
+ "params": [
+ {
+ "codeHash": "0x9bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce8",
+ "hashType": "type",
+ "args": "0xb39bbc0b3673c7d36450bc14cfcdad2d559c6c64"
+ }
+ ],
+ "expected": "ckb1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xwsqdnnw7qkdnnclfkg59uzn8umtfd2kwxceqxwquc4"
+ },
+ "full version testnet address identifies hash_type = type": {
+ "params": [
+ {
+ "codeHash": "0x9bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce8",
+ "hashType": "type",
+ "args": "0xb39bbc0b3673c7d36450bc14cfcdad2d559c6c64"
+ },
+ false
+ ],
+ "expected": "ckt1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xwsqdnnw7qkdnnclfkg59uzn8umtfd2kwxceqgutnjd"
+ },
+ "full version mainnet address identifies hash_type = data1": {
+ "params": [
+ {
+ "codeHash": "0xa656f172b6b45c245307aeb5a7a37a176f002f6f22e92582c58bf7ba362e4176",
+ "hashType": "data1",
+ "args": "0xb39bbc0b3673c7d36450bc14cfcdad2d559c6c64"
+ }
+ ],
+ "expected": "ckb1qzn9dutjk669cfznq7httfar0gtk7qp0du3wjfvzck9l0w3k9eqhvq4nnw7qkdnnclfkg59uzn8umtfd2kwxceqyclaxy"
+ },
+ "full version testnet address identifies hash_type = data1": {
+ "params": [
+ {
+ "codeHash": "0xa656f172b6b45c245307aeb5a7a37a176f002f6f22e92582c58bf7ba362e4176",
+ "hashType": "data1",
+ "args": "0xb39bbc0b3673c7d36450bc14cfcdad2d559c6c64"
+ },
+ false
+ ],
+ "expected": "ckt1qzn9dutjk669cfznq7httfar0gtk7qp0du3wjfvzck9l0w3k9eqhvq4nnw7qkdnnclfkg59uzn8umtfd2kwxceq225jvu"
+ },
+ "should throw an error when args doesn't start with 0x": {
+ "params": [
+ {
+ "codeHash": "0x3419a1c09eb2567f6552ee7a8ecffd64155cffe0f1796e6e61ec088d740c1356",
+ "hashType": "type",
+ "args": "4fb2be2e5d0c1a3b8694f832350a33c1685d477a0c0101"
+ }
+ ],
+ "exception": "Hex string 4fb2be2e5d0c1a3b8694f832350a33c1685d477a0c0101 should start with 0x"
+ },
+ "should throw an error when code hash doesn't start with 0x": {
+ "params": [
+ {
+ "codeHash": "3419a1c09eb2567f6552ee7a8ecffd64155cffe0f1796e6e61ec088d740c1356",
+ "hashType": "type",
+ "args": "0x4fb2be2e5d0c1a3b8694f832350a33c1685d477a0c0101"
+ }
+ ],
+ "exception": "'3419a1c09eb2567f6552ee7a8ecffd64155cffe0f1796e6e61ec088d740c1356' is not a valid code hash"
+ },
+ "should throw an error when code hash has invalid length": {
+ "params": [
+ {
+ "codeHash": "0x3419a1c09eb2567f6552ee7a8ecffd64155cffe0f1796e6e61ec088d740c135",
+ "hashType": "type",
+ "args": "0x4fb2be2e5d0c1a3b8694f832350a33c1685d477a0c0101"
+ }
+ ],
+ "exception": "'0x3419a1c09eb2567f6552ee7a8ecffd64155cffe0f1796e6e61ec088d740c135' is not a valid code hash"
+ },
+ "should throw an error when hash type is inavlid": {
+ "params": [
+ {
+ "codeHash": "0x3419a1c09eb2567f6552ee7a8ecffd64155cffe0f1796e6e61ec088d740c1356",
+ "hashType": "type1",
+ "args": "0x4fb2be2e5d0c1a3b8694f832350a33c1685d477a0c0101"
+ }
+ ],
+ "exception": "'type1' is not a valid hash type"
}
}
}
diff --git a/packages/ckb-sdk-utils/__tests__/address/index.test.js b/packages/ckb-sdk-utils/__tests__/address/index.test.js
index f1adcbd2..cfb25da8 100644
--- a/packages/ckb-sdk-utils/__tests__/address/index.test.js
+++ b/packages/ckb-sdk-utils/__tests__/address/index.test.js
@@ -8,6 +8,7 @@ const {
parseAddress,
fullPayloadToAddress,
addressToScript,
+ scriptToAddress,
} = ckbUtils
describe('Test address module', () => {
@@ -120,4 +121,22 @@ describe('Test address module', () => {
}
})
})
+
+ describe('scriptToAddress', () => {
+ const fixtureTable = Object.entries(fixtures.scriptToAddress).map(([title, { params, expected, exception }]) => [
+ title,
+ params,
+ expected,
+ exception,
+ ])
+ test.each(fixtureTable)(`%s`, (_title, params, expected, exception) => {
+ expect.assertions(1)
+ try {
+ const actual = scriptToAddress(...params)
+ expect(actual).toEqual(expected)
+ } catch (err) {
+ expect(err).toEqual(new Error(exception))
+ }
+ })
+ })
})
diff --git a/packages/ckb-sdk-utils/__tests__/exceptions/fixtures.json b/packages/ckb-sdk-utils/__tests__/exceptions/fixtures.json
index 1aa66a4a..09ca82af 100644
--- a/packages/ckb-sdk-utils/__tests__/exceptions/fixtures.json
+++ b/packages/ckb-sdk-utils/__tests__/exceptions/fixtures.json
@@ -38,14 +38,28 @@
"params": ["Invalid Payload", "short"],
"expected": {
"code": 104,
- "message": "Invalid Payload is not a valid short version address payload"
+ "message": "'Invalid Payload' is not a valid short version address payload"
}
},
"AddressException": {
- "params": ["Invalid Address", "full"],
+ "params": ["Invalid Address", "", "full"],
"expected": {
"code": 104,
- "message": "Invalid Address is not a valid full version address"
+ "message": "'Invalid Address' is not a valid full version address"
+ }
+ },
+ "CodeHashException": {
+ "params": ["0x"],
+ "expected": {
+ "code": 104,
+ "message": "'0x' is not a valid code hash"
+ }
+ },
+ "HashTypeException": {
+ "params": ["0x03"],
+ "expected": {
+ "code": 104,
+ "message": "'0x03' is not a valid hash type"
}
},
"OutLenTooSmallException": {
diff --git a/packages/ckb-sdk-utils/__tests__/exceptions/index.test.js b/packages/ckb-sdk-utils/__tests__/exceptions/index.test.js
index e2cd38c2..7b9577b2 100644
--- a/packages/ckb-sdk-utils/__tests__/exceptions/index.test.js
+++ b/packages/ckb-sdk-utils/__tests__/exceptions/index.test.js
@@ -1,7 +1,7 @@
const exceptions = require('../../lib/exceptions')
const fixtures = require('./fixtures.json')
-describe.only('Test exceptions', () => {
+describe('Test exceptions', () => {
const fixtureTable = Object.entries(fixtures).map(([exceptionName, { params, expected }]) => [
exceptionName,
params,
diff --git a/packages/ckb-sdk-utils/__tests__/serialization/transaction/fixtures.json b/packages/ckb-sdk-utils/__tests__/serialization/transaction/fixtures.json
index 9cf5bf2c..0f2fb440 100644
--- a/packages/ckb-sdk-utils/__tests__/serialization/transaction/fixtures.json
+++ b/packages/ckb-sdk-utils/__tests__/serialization/transaction/fixtures.json
@@ -332,6 +332,64 @@
]
},
"expected": "0x120200000c000000c5010000b90100001c000000200000006e00000072000000a2000000a50100000000000002000000c12386705b5cbb312b693874f3edf45c43a274482e27b8df0fd80c8d3f5feb8b00000000010fb4945d52baf91e0dee2a686cdd9d84cad95b566a1d7409b970ee0a0f364f6002000000000000000001000000000000000000000031f695263423a4b05045dd25ce6692bb55d7bba2965d8be16b036e138e72cc6501000000030100000c000000a20000009600000010000000180000006100000000e87648170000004900000010000000300000003100000068d5438ac952d2f584abf879527946a537e82c7f3c1cbf6d8ebf9767437d8e88011400000059a27ef3ba84f061517d13f42cf44ed02061006135000000100000003000000031000000ece45e0979030e2f8909f76258631c42333b1e906fd9701ec3600a464a90b8f600000000006100000010000000180000006100000000506a41e15900004900000010000000300000003100000068d5438ac952d2f584abf879527946a537e82c7f3c1cbf6d8ebf9767437d8e88011400000059a27ef3ba84f061517d13f42cf44ed020610061140000000c0000001000000000000000000000004d000000080000004100000082df73581bcd08cb9aa270128d15e79996229ce8ea9e4f985b49fbf36762c5c37936caf3ea3784ee326f60b8992924fcf496f9503c907982525a3436f01ab32900"
+ },
+ "transaction containing data1 lock script": {
+ "transaction": {
+ "version": "0x0",
+ "cellDeps": [
+ {
+ "outPoint": {
+ "txHash": "0xace5ea83c478bb866edf122ff862085789158f5cbff155b7bb5f13058555b708",
+ "index": "0x0"
+ },
+ "depType": "depGroup"
+ }
+ ],
+ "headerDeps": [],
+ "inputs": [
+ {
+ "since": "0x0",
+ "previousOutput": {
+ "txHash": "0xa563884b3686078ec7e7677a5f86449b15cf2693f3c1241766c6996f206cc541",
+ "index": "0x7"
+ }
+ }
+ ],
+ "outputs": [
+ {
+ "capacity": "0x2540be400",
+ "lock": {
+ "codeHash": "0x709f3fda12f561cfacf92273c57a98fede188a3f1a59b1f888d113f9cce08649",
+ "hashType": "data",
+ "args": "0xc8328aabcd9b9e8e64fbc566c4385c3bdeb219d7"
+ },
+ "type": null
+ },
+ {
+ "capacity": "0x2540be400",
+ "lock": {
+ "codeHash": "0x9bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce8",
+ "hashType": "type",
+ "args": "0xc8328aabcd9b9e8e64fbc566c4385c3bdeb219d7"
+ },
+ "type": null
+ },
+ {
+ "capacity": "0x2540be400",
+ "lock": {
+ "codeHash": "0x709f3fda12f561cfacf92273c57a98fede188a3f1a59b1f888d113f9cce08649",
+ "hashType": "data1",
+ "args": "0xc8328aabcd9b9e8e64fbc566c4385c3bdeb219d7"
+ },
+ "type": null
+ }
+ ],
+ "outputsData": ["0x", "0x", "0x"],
+ "witnesses": [
+ "0x550000001000000055000000550000004100000070b823564f7d1f814cc135ddd56fd8e8931b3a7040eaf1fb828adae29736a3cb0bc7f65021135b293d10a22da61fcc64f7cb660bf2c3276ad63630dad0b6099001"
+ ]
+ },
+ "expected": "0x390200000c000000d8010000cc0100001c00000020000000490000004d0000007d000000b00100000000000001000000ace5ea83c478bb866edf122ff862085789158f5cbff155b7bb5f13058555b708000000000100000000010000000000000000000000a563884b3686078ec7e7677a5f86449b15cf2693f3c1241766c6996f206cc54107000000330100001000000071000000d20000006100000010000000180000006100000000e40b540200000049000000100000003000000031000000709f3fda12f561cfacf92273c57a98fede188a3f1a59b1f888d113f9cce086490014000000c8328aabcd9b9e8e64fbc566c4385c3bdeb219d76100000010000000180000006100000000e40b5402000000490000001000000030000000310000009bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce80114000000c8328aabcd9b9e8e64fbc566c4385c3bdeb219d76100000010000000180000006100000000e40b540200000049000000100000003000000031000000709f3fda12f561cfacf92273c57a98fede188a3f1a59b1f888d113f9cce086490214000000c8328aabcd9b9e8e64fbc566c4385c3bdeb219d71c000000100000001400000018000000000000000000000000000000610000000800000055000000550000001000000055000000550000004100000070b823564f7d1f814cc135ddd56fd8e8931b3a7040eaf1fb828adae29736a3cb0bc7f65021135b293d10a22da61fcc64f7cb660bf2c3276ad63630dad0b6099001"
}
}
}
diff --git a/packages/ckb-sdk-utils/__tests__/utils/rawTransactionToHash.fixtures.json b/packages/ckb-sdk-utils/__tests__/utils/rawTransactionToHash.fixtures.json
index ce73d139..b9359359 100644
--- a/packages/ckb-sdk-utils/__tests__/utils/rawTransactionToHash.fixtures.json
+++ b/packages/ckb-sdk-utils/__tests__/utils/rawTransactionToHash.fixtures.json
@@ -60,6 +60,64 @@
"hash": "0x9d1bf801b235ce62812844f01381a070c0cc72876364861e00492eac1d8b54e7"
},
"expected": "0xe765f9912b06c72552dae11779f6371309236e968aa045ae3b8f426d8ec8ca05"
+ },
+ {
+ "rawTransaction": {
+ "version": "0x0",
+ "cellDeps": [
+ {
+ "outPoint": {
+ "txHash": "0xace5ea83c478bb866edf122ff862085789158f5cbff155b7bb5f13058555b708",
+ "index": "0x0"
+ },
+ "depType": "depGroup"
+ }
+ ],
+ "headerDeps": [],
+ "inputs": [
+ {
+ "since": "0x0",
+ "previousOutput": {
+ "txHash": "0xa563884b3686078ec7e7677a5f86449b15cf2693f3c1241766c6996f206cc541",
+ "index": "0x7"
+ }
+ }
+ ],
+ "outputs": [
+ {
+ "capacity": "0x2540be400",
+ "lock": {
+ "codeHash": "0x709f3fda12f561cfacf92273c57a98fede188a3f1a59b1f888d113f9cce08649",
+ "hashType": "data",
+ "args": "0xc8328aabcd9b9e8e64fbc566c4385c3bdeb219d7"
+ },
+ "type": null
+ },
+ {
+ "capacity": "0x2540be400",
+ "lock": {
+ "codeHash": "0x9bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce8",
+ "hashType": "type",
+ "args": "0xc8328aabcd9b9e8e64fbc566c4385c3bdeb219d7"
+ },
+ "type": null
+ },
+ {
+ "capacity": "0x2540be400",
+ "lock": {
+ "codeHash": "0x709f3fda12f561cfacf92273c57a98fede188a3f1a59b1f888d113f9cce08649",
+ "hashType": "data1",
+ "args": "0xc8328aabcd9b9e8e64fbc566c4385c3bdeb219d7"
+ },
+ "type": null
+ }
+ ],
+ "outputsData": ["0x", "0x", "0x"],
+ "witnesses": [
+ "0x550000001000000055000000550000004100000070b823564f7d1f814cc135ddd56fd8e8931b3a7040eaf1fb828adae29736a3cb0bc7f65021135b293d10a22da61fcc64f7cb660bf2c3276ad63630dad0b6099001"
+ ]
+ },
+ "expected": "0x9110ca9266f89938f09ae6f93cc914b2c856cc842440d56fda6d16ee62543f5c"
}
]
}
diff --git a/packages/ckb-sdk-utils/package.json b/packages/ckb-sdk-utils/package.json
index a0271e6a..4d918565 100644
--- a/packages/ckb-sdk-utils/package.json
+++ b/packages/ckb-sdk-utils/package.json
@@ -32,6 +32,7 @@
},
"dependencies": {
"@nervosnetwork/ckb-types": "0.43.0",
+ "bech32": "2.0.0",
"elliptic": "6.5.4",
"jsbi": "3.1.3",
"tslib": "2.3.1"
diff --git a/packages/ckb-sdk-utils/src/address/index.ts b/packages/ckb-sdk-utils/src/address/index.ts
index f00dc8c9..bedd6abf 100644
--- a/packages/ckb-sdk-utils/src/address/index.ts
+++ b/packages/ckb-sdk-utils/src/address/index.ts
@@ -1,4 +1,4 @@
-import { bech32, blake160 } from '..'
+import { blake160, bech32, bech32m } from '..'
import {
SECP256K1_BLAKE160,
SECP256K1_MULTISIG,
@@ -6,7 +6,18 @@ import {
ANYONE_CAN_PAY_TESTNET,
} from '../systemScripts'
import { hexToBytes, bytesToHex } from '../convertors'
-import { HexStringWithout0xException, AddressException, AddressPayloadException } from '../exceptions'
+import {
+ HexStringWithout0xException,
+ AddressException,
+ AddressPayloadException,
+ CodeHashException,
+ HashTypeException,
+ ParameterRequiredException,
+} from '../exceptions'
+
+const MAX_BECH32_LIMIT = 1023
+
+// TODO: deprecate outdated methods
export enum AddressPrefix {
Mainnet = 'ckb',
@@ -14,11 +25,55 @@ export enum AddressPrefix {
}
export enum AddressType {
+ FullVersion = '0x00', // full version identifies the hash_type
HashIdx = '0x01', // short version for locks with popular codehash
- DataCodeHash = '0x02', // full version with hash type 'Data'
- TypeCodeHash = '0x04', // full version with hash type 'Type'
+ DataCodeHash = '0x02', // full version with hash type 'Data', deprecated
+ TypeCodeHash = '0x04', // full version with hash type 'Type', deprecated
}
+/**
+ * @description payload to a full address of new version
+ */
+const payloadToAddress = (payload: Uint8Array, isMainnet = true) =>
+ bech32m.encode(isMainnet ? AddressPrefix.Mainnet : AddressPrefix.Testnet, bech32m.toWords(payload), MAX_BECH32_LIMIT)
+
+const scriptToPayload = ({ codeHash, hashType, args }: CKBComponents.Script): Uint8Array => {
+ if (!args.startsWith('0x')) {
+ throw new HexStringWithout0xException(args)
+ }
+
+ if (!codeHash.startsWith('0x') || codeHash.length !== 66) {
+ throw new CodeHashException(codeHash)
+ }
+
+ enum HashType {
+ data = '00',
+ type = '01',
+ data1 = '02',
+ }
+
+ if (!HashType[hashType]) {
+ throw new HashTypeException(hashType)
+ }
+
+ return hexToBytes(`0x00${codeHash.slice(2)}${HashType[hashType]}${args.slice(2)}`)
+}
+
+/**
+ * @function scriptToAddress
+ * @description The only way recommended to generated a full address of new version
+ * @param {object} script
+ * @param {booealn} isMainnet
+ * @returns {string} address
+ */
+export const scriptToAddress = (script: CKBComponents.Script, isMainnet = true) =>
+ payloadToAddress(scriptToPayload(script), isMainnet)
+
+/**
+ * 0x00 SECP256K1 + blake160
+ * 0x01 SECP256k1 + multisig
+ * 0x02 anyone_can_pay
+ */
export type CodeHashIndex = '0x00' | '0x01' | '0x02'
export interface AddressOptions {
@@ -29,7 +84,8 @@ export interface AddressOptions {
/**
* @function toAddressPayload
- * @description payload = type(01) | code hash index(00) | args(blake160-formatted pubkey)
+ * @description obsolete payload = type(01) | code hash index(00) | args(blake160-formatted pubkey)
+ * new payload = type(00) | code hash | hash type(00|01|02) | args
* @see https://github.com/nervosnetwork/rfcs/blob/master/rfcs/0021-ckb-address-format/0021-ckb-address-format.md
* @param {string | Uint8Array} args, use as the identifier of an address, usually the public key hash is used.
* @param {string} type, used to indicate which format is adopted to compose the address.
@@ -39,18 +95,44 @@ export const toAddressPayload = (
args: string | Uint8Array,
type: AddressType = AddressType.HashIdx,
codeHashOrCodeHashIndex: CodeHashIndex | CKBComponents.Hash256 = '0x00',
+ hashType?: CKBComponents.ScriptHashType,
): Uint8Array => {
- if (typeof args === 'string') {
- if (!args.startsWith('0x')) {
- throw new HexStringWithout0xException(args)
- }
- return new Uint8Array([...hexToBytes(type), ...hexToBytes(codeHashOrCodeHashIndex), ...hexToBytes(args)])
+ if (typeof args === 'string' && !args.startsWith('0x')) {
+ throw new HexStringWithout0xException(args)
}
- return new Uint8Array([...hexToBytes(type), ...hexToBytes(codeHashOrCodeHashIndex), ...args])
+
+ if ([AddressType.DataCodeHash, AddressType.TypeCodeHash].includes(type)) {
+ /* eslint-disable max-len */
+ console.warn(
+ `Address of 'AddressType.DataCodeHash' or 'AddressType.TypeCodeHash' is deprecated, please use address of AddressPrefix.FullVersion`,
+ )
+ }
+
+ if (type !== AddressType.FullVersion) {
+ return new Uint8Array([
+ ...hexToBytes(type),
+ ...hexToBytes(codeHashOrCodeHashIndex),
+ ...(typeof args === 'string' ? hexToBytes(args) : args),
+ ])
+ }
+
+ if (!codeHashOrCodeHashIndex.startsWith('0x') || codeHashOrCodeHashIndex.length !== 66) {
+ throw new CodeHashException(codeHashOrCodeHashIndex)
+ }
+
+ if (!hashType) {
+ throw new ParameterRequiredException('hashType')
+ }
+
+ return scriptToPayload({
+ codeHash: codeHashOrCodeHashIndex,
+ hashType,
+ args: typeof args === 'string' ? args : bytesToHex(args),
+ })
}
/**
- * @name bech32Address
+ * @function bech32Address
* @description generate the address by bech32 algorithm
* @param args, used as the identifier of an address, usually the public key hash is used.
* @param {[string]} prefix, the prefix precedes the address, default to be ckb.
@@ -61,11 +143,12 @@ export const toAddressPayload = (
export const bech32Address = (
args: Uint8Array | string,
{ prefix = AddressPrefix.Mainnet, type = AddressType.HashIdx, codeHashOrCodeHashIndex = '0x00' }: AddressOptions = {},
-) => bech32.encode(prefix, bech32.toWords(toAddressPayload(args, type, codeHashOrCodeHashIndex)))
+) => bech32.encode(prefix, bech32.toWords(toAddressPayload(args, type, codeHashOrCodeHashIndex)), MAX_BECH32_LIMIT)
/**
+ * @deprecated
* @name fullPayloadToAddress
- * @description generate the address with payload in full version format.
+ * @description deprecated method to generate the address with payload in full version format. Use scriptToAddress instead.
* @param {string} args, used as the identifier of an address.
* @param {[string]} prefix, the prefix precedes the address, default to be ckb.
* @param {[string]} type, used to indicate which format the address conforms to, default to be 0x02,
@@ -115,8 +198,9 @@ const isValidShortVersionPayload = (payload: Uint8Array) => {
/* eslint-enable indent */
}
-const isValidPayload = (payload: Uint8Array) => {
- const [type, ...data] = payload
+const isPayloadValid = (payload: Uint8Array) => {
+ const type = payload[0]
+ const data = payload.slice(1)
/* eslint-disable indent */
switch (type) {
case +AddressType.HashIdx: {
@@ -130,6 +214,19 @@ const isValidPayload = (payload: Uint8Array) => {
}
break
}
+ case +AddressType.FullVersion: {
+ const codeHash = data.slice(0, 32)
+ if (codeHash.length < 32) {
+ throw new CodeHashException(bytesToHex(codeHash))
+ }
+
+ const hashType = parseInt(data[32].toString(), 16)
+ if (hashType > 2) {
+ throw new HashTypeException(`0x${hashType.toString(16)}`)
+ }
+
+ break
+ }
default: {
throw new AddressPayloadException(payload)
}
@@ -148,12 +245,18 @@ export declare interface ParseAddress {
* e.g. 0x | 01 | 00 | e2fa82e70b062c8644b80ad7ecf6e015e5f352f6
*/
export const parseAddress: ParseAddress = (address: string, encode: 'binary' | 'hex' = 'binary'): any => {
- const decoded = bech32.decode(address)
- const payload = bech32.fromWords(new Uint8Array(decoded.words))
+ let payload: Uint8Array = new Uint8Array()
try {
- isValidPayload(payload)
+ const decoded = bech32.decode(address, MAX_BECH32_LIMIT)
+ payload = new Uint8Array(bech32.fromWords(new Uint8Array(decoded.words)))
+ } catch {
+ const decoded = bech32m.decode(address, MAX_BECH32_LIMIT)
+ payload = new Uint8Array(bech32m.fromWords(new Uint8Array(decoded.words)))
+ }
+ try {
+ isPayloadValid(payload)
} catch (err) {
- throw new AddressException(address, err.type)
+ throw new AddressException(address, err.stack, err.type)
}
return encode === 'binary' ? payload : bytesToHex(payload)
}
@@ -162,6 +265,20 @@ export const addressToScript = (address: string): CKBComponents.Script => {
const payload = parseAddress(address)
const type = payload[0]
+ if (type === +AddressType.FullVersion) {
+ const HASH_TYPE: Record = {
+ '00': 'data',
+ '01': 'type',
+ '02': 'data1',
+ }
+ const p = bytesToHex(payload)
+
+ const codeHash = `0x${p.substr(4, 64)}`
+ const hashType = HASH_TYPE[p.substr(68, 2)]
+ const args = `0x${p.substr(70)}`
+ return { codeHash, hashType, args }
+ }
+
if (type === +AddressType.HashIdx) {
const codeHashIndices = [
SECP256K1_BLAKE160,
diff --git a/packages/ckb-sdk-utils/src/crypto/bech32.ts b/packages/ckb-sdk-utils/src/crypto/bech32.ts
deleted file mode 100644
index c7aaaf13..00000000
--- a/packages/ckb-sdk-utils/src/crypto/bech32.ts
+++ /dev/null
@@ -1,160 +0,0 @@
-const ALPHABET = 'qpzry9x8gf2tvdw0s3jn54khce6mua7l'
-
-const SEPARATOR = '1'
-
-const alphabetMap = new Map()
-
-for (let i = 0; i < ALPHABET.length; i++) {
- const char = ALPHABET.charAt(i)
-
- if (alphabetMap.get(char) !== undefined) {
- throw new TypeError(`${char} is ambiguous`)
- }
-
- alphabetMap.set(char, i)
-}
-
-const polymodStep = (values: any) => {
- const b = values >> 25
- return (
- ((values & 0x1ffffff) << 5) ^
- (-((b >> 0) & 1) & 0x3b6a57b2) ^
- (-((b >> 1) & 1) & 0x26508e6d) ^
- (-((b >> 2) & 1) & 0x1ea119fa) ^
- (-((b >> 3) & 1) & 0x3d4233dd) ^
- (-((b >> 4) & 1) & 0x2a1462b3)
- )
-}
-
-const prefixChecksum = (prefix: string) => {
- let checksum = 1
-
- for (let i = 0; i < prefix.length; ++i) {
- const c = prefix.charCodeAt(i)
- if (c < 33 || c > 126) throw new Error(`Invalid prefix (${prefix})`)
- checksum = polymodStep(checksum) ^ (c >> 5)
- }
-
- checksum = polymodStep(checksum)
-
- for (let i = 0; i < prefix.length; ++i) {
- const v = prefix.charCodeAt(i)
- checksum = polymodStep(checksum) ^ (v & 0x1f)
- }
-
- return checksum
-}
-
-export const encode = (prefix: string, words: Uint8Array) => {
- const formattedPrefix = prefix.toLowerCase()
-
- // determine checksum mod
- let checksum = prefixChecksum(formattedPrefix)
-
- let result = `${formattedPrefix}${SEPARATOR}`
-
- for (let i = 0; i < words.length; ++i) {
- const x = words[i]
- if (x >> 5 !== 0) throw new Error('Non 5-bit word')
-
- checksum = polymodStep(checksum) ^ x
-
- result += ALPHABET.charAt(x)
- }
-
- for (let i = 0; i < 6; ++i) {
- checksum = polymodStep(checksum)
- }
-
- checksum ^= 1
-
- for (let i = 0; i < 6; ++i) {
- const v = (checksum >> ((5 - i) * 5)) & 0x1f
- result += ALPHABET.charAt(v)
- }
-
- return result
-}
-
-export const decode = (encoded: string) => {
- const lowered = encoded.toLowerCase()
-
- const uppered = encoded.toUpperCase()
-
- if (encoded !== lowered && encoded !== uppered) throw new Error(`Mixed-case string ${encoded}`)
-
- const str = lowered
-
- if (str.length < 8) throw new TypeError(`${str} too short`)
-
- const split = str.lastIndexOf(SEPARATOR)
-
- if (split === -1) throw new Error(`No separator character for ${str}`)
-
- if (split === 0) throw new Error(`Missing prefix for ${str}`)
-
- const prefix = str.slice(0, split)
-
- const wordChars = str.slice(split + 1)
-
- if (wordChars.length < 6) throw new Error('Data too short')
-
- let checksum = prefixChecksum(prefix)
-
- const words: number[] = []
-
- wordChars.split('').forEach((_, i) => {
- const c = wordChars.charAt(i)
- const v = alphabetMap.get(c)
- if (v === undefined) throw new Error(`Unknown character ${c}`)
- checksum = polymodStep(checksum) ^ v
- if (i + 6 < wordChars.length) {
- words.push(v)
- }
- })
-
- if (checksum !== 1) throw new Error(`Invalid checksum for ${str}`)
- return {
- prefix,
- words,
- }
-}
-
-const convert = (data: Uint8Array, inBits: number, outBits: number, pad: boolean): Uint8Array => {
- let value = 0
- let bits = 0
- const maxV = (1 << outBits) - 1
-
- const result = []
- for (let i = 0; i < data.length; ++i) {
- value = (value << inBits) | data[i]
- bits += inBits
-
- while (bits >= outBits) {
- bits -= outBits
- result.push((value >> bits) & maxV)
- }
- }
-
- if (pad) {
- if (bits > 0) {
- result.push((value << (outBits - bits)) & maxV)
- }
- } else {
- if (bits >= inBits) throw new Error('Excess padding')
- if ((value << (outBits - bits)) & maxV) throw new Error('Non-zero padding')
- }
-
- return new Uint8Array(result)
-}
-
-export const toWords = (bytes: Uint8Array) => convert(bytes, 8, 5, true)
-
-export const fromWords = (words: Uint8Array) => convert(words, 5, 8, false)
-
-export default {
- decode,
- encode,
- toWords,
- fromWords,
-}
diff --git a/packages/ckb-sdk-utils/src/crypto/index.ts b/packages/ckb-sdk-utils/src/crypto/index.ts
index 40a6d188..784cbc4f 100644
--- a/packages/ckb-sdk-utils/src/crypto/index.ts
+++ b/packages/ckb-sdk-utils/src/crypto/index.ts
@@ -1,15 +1,17 @@
+import { bech32, bech32m } from 'bech32'
import blake2b from './blake2b'
-import bech32 from './bech32'
import blake160 from './blake160'
module.exports = {
blake2b,
blake160,
bech32,
+ bech32m,
}
export default {
blake2b,
blake160,
bech32,
+ bech32m,
}
diff --git a/packages/ckb-sdk-utils/src/exceptions/address.ts b/packages/ckb-sdk-utils/src/exceptions/address.ts
index 145be75a..4b93b770 100644
--- a/packages/ckb-sdk-utils/src/exceptions/address.ts
+++ b/packages/ckb-sdk-utils/src/exceptions/address.ts
@@ -6,7 +6,7 @@ export class AddressPayloadException extends Error {
type: 'short' | 'full' | undefined
constructor(payload: Uint8Array, type?: 'short' | 'full') {
- super(`${payload} is not a valid ${type ? `${type} version ` : ''}address payload`)
+ super(`'${payload}' is not a valid ${type ? `${type} version ` : ''}address payload`)
this.type = type
}
}
@@ -16,13 +16,32 @@ export class AddressException extends Error {
type: 'short' | 'full' | undefined
- constructor(addr: string, type?: 'short' | 'full') {
- super(`${addr} is not a valid ${type ? `${type} version ` : ''}address`)
+ constructor(addr: string, stack: string, type?: 'short' | 'full') {
+ super(`'${addr}' is not a valid ${type ? `${type} version ` : ''}address`)
this.type = type
+ this.stack = stack
+ }
+}
+
+export class CodeHashException extends Error {
+ code = ErrorCode.AddressInvalid
+
+ constructor(codeHash: string) {
+ super(`'${codeHash}' is not a valid code hash`)
+ }
+}
+
+export class HashTypeException extends Error {
+ code = ErrorCode.AddressInvalid
+
+ constructor(hashType: string) {
+ super(`'${hashType}' is not a valid hash type`)
}
}
export default {
AddressPayloadException,
AddressException,
+ CodeHashException,
+ HashTypeException,
}
diff --git a/packages/ckb-sdk-utils/src/index.ts b/packages/ckb-sdk-utils/src/index.ts
index d444b7f8..c8843628 100644
--- a/packages/ckb-sdk-utils/src/index.ts
+++ b/packages/ckb-sdk-utils/src/index.ts
@@ -17,7 +17,7 @@ export * as systemScripts from './systemScripts'
export * as reconcilers from './reconcilers'
export { serializeScript, serializeRawTransaction, serializeTransaction, serializeWitnessArgs, JSBI, PERSONAL }
-export const { blake2b, bech32, blake160 } = crypto
+export const { blake2b, bech32, bech32m, blake160 } = crypto
export const scriptToHash = (script: CKBComponents.Script) => {
if (!script) throw new ParameterRequiredException('Script')
diff --git a/packages/ckb-sdk-utils/src/serialization/script.ts b/packages/ckb-sdk-utils/src/serialization/script.ts
index 5caf4ecd..2ed1efe7 100644
--- a/packages/ckb-sdk-utils/src/serialization/script.ts
+++ b/packages/ckb-sdk-utils/src/serialization/script.ts
@@ -6,6 +6,7 @@ export const serializeCodeHash = (codeHash: CKBComponents.Hash256) => serializeA
export const serializeHashType = (hashType: CKBComponents.ScriptHashType) => {
if (hashType === 'data') return '0x00'
if (hashType === 'type') return '0x01'
+ if (hashType === 'data1') return '0x02'
throw new TypeError("Hash type must be either of 'data' or 'type'")
}
diff --git a/packages/ckb-types/index.d.ts b/packages/ckb-types/index.d.ts
index 96209de1..c3b91417 100644
--- a/packages/ckb-types/index.d.ts
+++ b/packages/ckb-types/index.d.ts
@@ -30,7 +30,7 @@ declare namespace CKBComponents {
Committed = 'committed',
}
- export type ScriptHashType = 'data' | 'type'
+ export type ScriptHashType = 'data' | 'type' | 'data1'
export type DepType = 'code' | 'depGroup'
diff --git a/yarn.lock b/yarn.lock
index 39dc9e49..94cd22a4 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2045,6 +2045,11 @@ bcrypt-pbkdf@^1.0.0:
dependencies:
tweetnacl "^0.14.3"
+bech32@2.0.0:
+ version "2.0.0"
+ resolved "https://registry.npmjs.org/bech32/-/bech32-2.0.0.tgz#078d3686535075c8c79709f054b1b226a133b355"
+ integrity sha512-LcknSilhIGatDAsY1ak2I8VtGaHNhgMSYVxFrGLXv+xLHytaKZKcaUJJUE7qmBr7h33o5YQwP55pMI0xmkpJwg==
+
bech32@^1.1.2:
version "1.1.4"
resolved "https://registry.npmjs.org/bech32/-/bech32-1.1.4.tgz#e38c9f37bf179b8eb16ae3a772b40c356d4832e9"