Comparing version 2.4.0 to 2.5.0
@@ -107,3 +107,3 @@ import fs from 'fs'; | ||
const { r, s } = ec.sign(starkKeyPair, msgHash); | ||
const signature = ec.sign(starkKeyPair, msgHash); | ||
const { code, transaction_hash } = await wallet.invoke( | ||
@@ -117,3 +117,3 @@ 'execute', | ||
}, | ||
[number.toHex(r), number.toHex(s)] | ||
signature | ||
); | ||
@@ -156,3 +156,3 @@ | ||
const { r, s } = ec.sign(keyPair, msgHash); | ||
const [r, s] = ec.sign(keyPair, msgHash); | ||
expect(r.toString()).toBe( | ||
@@ -159,0 +159,0 @@ '706800951915233622090196542158919402159816118214143837213294331713137614072' |
@@ -43,3 +43,3 @@ import { ec, getKeyPair, getStarkKey, sign, verify } from '../../src/utils/ellipticCurve'; | ||
const keyPair = getKeyPair(privateKey); | ||
const { r, s } = sign(keyPair, removeHexPrefix(hashMsg)); | ||
const [r, s] = sign(keyPair, removeHexPrefix(hashMsg)); | ||
expect(r.toString()).toStrictEqual( | ||
@@ -46,0 +46,0 @@ toBN('2458502865976494910213617956670505342647705497324144349552978333078363662855').toString() |
@@ -0,1 +1,17 @@ | ||
# [2.5.0](https://github.com/seanjameshan/starknet.js/compare/v2.4.0...v2.5.0) (2021-12-13) | ||
### Bug Fixes | ||
- add jsdoc comment ([4cd969f](https://github.com/seanjameshan/starknet.js/commit/4cd969f82eb4a5d8c08feb59c42fb3e7195af50e)) | ||
- remove eip712 reference ([039a360](https://github.com/seanjameshan/starknet.js/commit/039a360873f9a1cdedc7a498b6e1732183957143)) | ||
- remove unused types ([e528f7d](https://github.com/seanjameshan/starknet.js/commit/e528f7d75f4560d2affe3ca99426e01fbee6dfb5)) | ||
- review ([a3813c9](https://github.com/seanjameshan/starknet.js/commit/a3813c9931c178d58c2521b926fb3fdff6944635)) | ||
### Features | ||
- add `getKeyPairFromPublicKey` method ([66d543d](https://github.com/seanjameshan/starknet.js/commit/66d543dca1bb302654f0f1588a27f7794bfa49be)) | ||
- add tests ([b414a83](https://github.com/seanjameshan/starknet.js/commit/b414a839a1fdd56084a58d6efb4747e8f5455628)) | ||
- allow multi sig ([fc1e086](https://github.com/seanjameshan/starknet.js/commit/fc1e0866154d2bf29f26374639a34fec438bae5e)) | ||
- support eip712 for starknet ([d597082](https://github.com/seanjameshan/starknet.js/commit/d59708211fc497d801699a7168dad1a5cc9648fd)) | ||
# [2.4.0](https://github.com/seanjameshan/starknet.js/compare/v2.3.1...v2.4.0) (2021-12-09) | ||
@@ -2,0 +18,0 @@ |
import { Provider } from './provider'; | ||
import { Abi, StructAbi } from './types'; | ||
import { Abi, Signature, StructAbi } from './types'; | ||
import { BigNumberish } from './utils/number'; | ||
@@ -36,5 +36,5 @@ export declare type Args = { | ||
args?: Args, | ||
signature?: [BigNumberish, BigNumberish] | ||
signature?: Signature | ||
): Promise<import('./types').AddTransactionResponse>; | ||
call(method: string, args?: Args): Promise<Args>; | ||
} |
import { Provider } from './provider'; | ||
import { Abi, StructAbi } from './types'; | ||
import { Abi, Signature, StructAbi } from './types'; | ||
import { BigNumberish } from './utils/number'; | ||
@@ -30,4 +30,4 @@ export declare type Args = { | ||
private parseResponse; | ||
invoke(method: string, args?: Args, signature?: [BigNumberish, BigNumberish]): Promise<import("./types").AddTransactionResponse>; | ||
invoke(method: string, args?: Args, signature?: Signature): Promise<import("./types").AddTransactionResponse>; | ||
call(method: string, args?: Args): Promise<Args>; | ||
} |
@@ -20,1 +20,2 @@ /** | ||
export * as shortString from './utils/shortString'; | ||
export * as typedData from './utils/typedData'; |
@@ -25,3 +25,3 @@ "use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.shortString = exports.uint256 = exports.ec = exports.stark = exports.number = exports.json = exports.hash = exports.encode = exports.constants = void 0; | ||
exports.typedData = exports.shortString = exports.uint256 = exports.ec = exports.stark = exports.number = exports.json = exports.hash = exports.encode = exports.constants = void 0; | ||
/** | ||
@@ -46,1 +46,2 @@ * Main | ||
exports.shortString = __importStar(require("./utils/shortString")); | ||
exports.typedData = __importStar(require("./utils/typedData")); |
@@ -1,2 +0,2 @@ | ||
import { AddTransactionResponse, CallContractResponse, CallContractTransaction, CompiledContract, GetBlockResponse, GetCodeResponse, GetContractAddressesResponse, GetTransactionResponse, GetTransactionStatusResponse, Transaction } from '../types'; | ||
import { AddTransactionResponse, CallContractResponse, CallContractTransaction, CompiledContract, GetBlockResponse, GetCodeResponse, GetContractAddressesResponse, GetTransactionResponse, GetTransactionStatusResponse, Signature, Transaction } from '../types'; | ||
import { BigNumberish } from '../utils/number'; | ||
@@ -107,5 +107,5 @@ import { ProviderInterface } from './interface'; | ||
*/ | ||
invokeFunction(contractAddress: string, entrypointSelector: string, calldata?: string[], signature?: [BigNumberish, BigNumberish]): Promise<AddTransactionResponse>; | ||
invokeFunction(contractAddress: string, entrypointSelector: string, calldata?: string[], signature?: Signature): Promise<AddTransactionResponse>; | ||
waitForTx(txHash: BigNumberish, retryInterval?: number): Promise<void>; | ||
} | ||
export {}; |
@@ -311,16 +311,17 @@ "use strict"; | ||
onchain = false; | ||
_a.label = 1; | ||
return [4 /*yield*/, wait(retryInterval)]; | ||
case 1: | ||
if (!!onchain) return [3 /*break*/, 4]; | ||
_a.sent(); | ||
_a.label = 2; | ||
case 2: | ||
if (!!onchain) return [3 /*break*/, 5]; | ||
// eslint-disable-next-line no-await-in-loop | ||
return [4 /*yield*/, wait(retryInterval)]; | ||
case 2: | ||
case 3: | ||
// eslint-disable-next-line no-await-in-loop | ||
_a.sent(); | ||
return [4 /*yield*/, this.getTransactionStatus(txHash)]; | ||
case 3: | ||
case 4: | ||
res = _a.sent(); | ||
if (res.tx_status === 'ACCEPTED_ONCHAIN' || | ||
(res.tx_status === 'PENDING' && res.block_hash !== 'pending') // This is needed as of today. In the future there will be a different status for pending transactions. | ||
) { | ||
if (res.tx_status === 'ACCEPTED_ON_L1' || res.tx_status === 'ACCEPTED_ON_L2') { | ||
onchain = true; | ||
@@ -334,4 +335,4 @@ } | ||
} | ||
return [3 /*break*/, 1]; | ||
case 4: return [2 /*return*/]; | ||
return [3 /*break*/, 2]; | ||
case 5: return [2 /*return*/]; | ||
} | ||
@@ -338,0 +339,0 @@ }); |
@@ -1,2 +0,2 @@ | ||
import type { AddTransactionResponse, CallContractResponse, CallContractTransaction, CompiledContract, GetBlockResponse, GetCodeResponse, GetContractAddressesResponse, GetTransactionResponse, GetTransactionStatusResponse, Transaction } from '../types'; | ||
import type { AddTransactionResponse, CallContractResponse, CallContractTransaction, CompiledContract, GetBlockResponse, GetCodeResponse, GetContractAddressesResponse, GetTransactionResponse, GetTransactionStatusResponse, Signature, Transaction } from '../types'; | ||
import type { BigNumberish } from '../utils/number'; | ||
@@ -98,4 +98,4 @@ export declare abstract class ProviderInterface { | ||
*/ | ||
abstract invokeFunction(contractAddress: string, entrypointSelector: string, calldata?: string[], signature?: [BigNumberish, BigNumberish]): Promise<AddTransactionResponse>; | ||
abstract invokeFunction(contractAddress: string, entrypointSelector: string, calldata?: string[], signature?: Signature): Promise<AddTransactionResponse>; | ||
abstract waitForTx(txHash: BigNumberish, retryInterval?: number): Promise<void>; | ||
} |
import { Provider } from '../provider'; | ||
import { AddTransactionResponse, KeyPair, Transaction } from '../types'; | ||
import { AddTransactionResponse, KeyPair, Signature, Transaction } from '../types'; | ||
import { TypedData } from '../utils/typedData'; | ||
import { SignerInterface } from './interface'; | ||
@@ -17,2 +18,18 @@ export declare class Signer extends Provider implements SignerInterface { | ||
addTransaction(transaction: Transaction): Promise<AddTransactionResponse>; | ||
/** | ||
* Sign an JSON object with the starknet private key and return the signature | ||
* | ||
* @param json - JSON object to be signed | ||
* @returns the signature of the JSON object | ||
* @throws {Error} if the JSON object is not a valid JSON | ||
*/ | ||
signMessage(typedData: TypedData): Promise<Signature>; | ||
/** | ||
* Hash a JSON object with pederson hash and return the hash | ||
* | ||
* @param json - JSON object to be hashed | ||
* @returns the hash of the JSON object | ||
* @throws {Error} if the JSON object is not a valid JSON | ||
*/ | ||
hashMessage(typedData: TypedData): Promise<string>; | ||
} |
@@ -90,2 +90,3 @@ "use strict"; | ||
var stark_1 = require("../utils/stark"); | ||
var typedData_1 = require("../utils/typedData"); | ||
var Signer = /** @class */ (function (_super) { | ||
@@ -109,5 +110,5 @@ __extends(Signer, _super); | ||
return __awaiter(this, void 0, void 0, function () { | ||
var nonceBn, result, calldataDecimal, msgHash, _a, r, s; | ||
return __generator(this, function (_b) { | ||
switch (_b.label) { | ||
var nonceBn, result, calldataDecimal, msgHash, signature; | ||
return __generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: | ||
@@ -125,9 +126,9 @@ if (transaction.type === 'DEPLOY') | ||
case 2: | ||
result = (_b.sent()).result; | ||
result = (_a.sent()).result; | ||
nonceBn = (0, number_1.toBN)(result[0]); | ||
_b.label = 3; | ||
_a.label = 3; | ||
case 3: | ||
calldataDecimal = (transaction.calldata || []).map(function (x) { return (0, number_1.toBN)(x).toString(); }); | ||
msgHash = (0, encode_1.addHexPrefix)((0, hash_1.hashMessage)(this.address, transaction.contract_address, transaction.entry_point_selector, calldataDecimal, nonceBn.toString())); | ||
_a = (0, ellipticCurve_1.sign)(this.keyPair, msgHash), r = _a.r, s = _a.s; | ||
signature = (0, ellipticCurve_1.sign)(this.keyPair, msgHash); | ||
return [2 /*return*/, _super.prototype.addTransaction.call(this, { | ||
@@ -144,3 +145,3 @@ type: 'INVOKE_FUNCTION', | ||
contract_address: this.address, | ||
signature: [r, s], | ||
signature: signature, | ||
})]; | ||
@@ -151,4 +152,39 @@ } | ||
}; | ||
/** | ||
* Sign an JSON object with the starknet private key and return the signature | ||
* | ||
* @param json - JSON object to be signed | ||
* @returns the signature of the JSON object | ||
* @throws {Error} if the JSON object is not a valid JSON | ||
*/ | ||
Signer.prototype.signMessage = function (typedData) { | ||
return __awaiter(this, void 0, void 0, function () { | ||
var _a, _b; | ||
return __generator(this, function (_c) { | ||
switch (_c.label) { | ||
case 0: | ||
_a = ellipticCurve_1.sign; | ||
_b = [this.keyPair]; | ||
return [4 /*yield*/, this.hashMessage(typedData)]; | ||
case 1: return [2 /*return*/, _a.apply(void 0, _b.concat([_c.sent()]))]; | ||
} | ||
}); | ||
}); | ||
}; | ||
/** | ||
* Hash a JSON object with pederson hash and return the hash | ||
* | ||
* @param json - JSON object to be hashed | ||
* @returns the hash of the JSON object | ||
* @throws {Error} if the JSON object is not a valid JSON | ||
*/ | ||
Signer.prototype.hashMessage = function (typedData) { | ||
return __awaiter(this, void 0, void 0, function () { | ||
return __generator(this, function (_a) { | ||
return [2 /*return*/, (0, typedData_1.getMessageHash)(typedData, this.address)]; | ||
}); | ||
}); | ||
}; | ||
return Signer; | ||
}(provider_1.Provider)); | ||
exports.Signer = Signer; |
import { Provider } from '../provider'; | ||
import { AddTransactionResponse, Transaction } from '../types'; | ||
import { AddTransactionResponse, Signature, Transaction } from '../types'; | ||
import { TypedData } from '../utils/typedData/types'; | ||
export declare abstract class SignerInterface extends Provider { | ||
@@ -14,2 +15,20 @@ abstract address: string; | ||
abstract addTransaction(transaction: Transaction): Promise<AddTransactionResponse>; | ||
/** | ||
* Sign an JSON object for off-chain usage with the starknet private key and return the signature | ||
* This adds a message prefix so it cant be interchanged with transactions | ||
* | ||
* @param json - JSON object to be signed | ||
* @returns the signature of the JSON object | ||
* @throws {Error} if the JSON object is not a valid JSON | ||
*/ | ||
abstract signMessage(typedData: TypedData): Promise<Signature>; | ||
/** | ||
* Hash a JSON object with pederson hash and return the hash | ||
* This adds a message prefix so it cant be interchanged with transactions | ||
* | ||
* @param json - JSON object to be hashed | ||
* @returns the hash of the JSON object | ||
* @throws {Error} if the JSON object is not a valid JSON | ||
*/ | ||
abstract hashMessage(typedData: TypedData): Promise<string>; | ||
} |
import type { ec as EC } from 'elliptic'; | ||
import type { BigNumberish } from './utils/number'; | ||
export declare type KeyPair = EC.KeyPair; | ||
export declare type Signature = EC.Signature; | ||
export declare type Signature = BigNumberish[]; | ||
export declare type GetContractAddressesResponse = { | ||
@@ -9,3 +9,3 @@ Starknet: string; | ||
}; | ||
export declare type Status = 'NOT_RECEIVED' | 'RECEIVED' | 'PENDING' | 'REJECTED' | 'ACCEPTED_ONCHAIN'; | ||
export declare type Status = 'NOT_RECEIVED' | 'RECEIVED' | 'PENDING' | 'ACCEPTED_ON_L2' | 'ACCEPTED_ON_L1' | 'REJECTED'; | ||
export declare type TransactionStatus = 'TRANSACTION_RECEIVED'; | ||
@@ -53,3 +53,3 @@ export declare type Type = 'DEPLOY' | 'INVOKE_FUNCTION'; | ||
contract_address: string; | ||
signature?: [BigNumberish, BigNumberish]; | ||
signature?: Signature; | ||
entry_point_type?: EntryPointType; | ||
@@ -56,0 +56,0 @@ entry_point_selector: string; |
@@ -8,3 +8,10 @@ import { ec as EC } from 'elliptic'; | ||
export declare function getStarkKey(keyPair: KeyPair): string; | ||
/** | ||
* Takes a public key and casts it into `elliptic` KeyPair format. | ||
* | ||
* @param publicKey - public key which should get casted to a KeyPair | ||
* @returns keyPair with public key only, which can be used to verify signatures, but cant sign anything | ||
*/ | ||
export declare function getKeyPairFromPublicKey(publicKey: BigNumberish): KeyPair; | ||
export declare function sign(keyPair: KeyPair, msgHash: string): Signature; | ||
export declare function verify(keyPair: KeyPair, msgHash: string, sig: Signature): boolean; | ||
export declare function verify(keyPair: KeyPair | KeyPair[], msgHash: string, sig: Signature): boolean; |
"use strict"; | ||
var __read = (this && this.__read) || function (o, n) { | ||
var m = typeof Symbol === "function" && o[Symbol.iterator]; | ||
if (!m) return o; | ||
var i = m.call(o), r, ar = [], e; | ||
try { | ||
while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); | ||
} | ||
catch (error) { e = { error: error }; } | ||
finally { | ||
try { | ||
if (r && !r.done && (m = i["return"])) m.call(i); | ||
} | ||
finally { if (e) throw e.error; } | ||
} | ||
return ar; | ||
}; | ||
var __importDefault = (this && this.__importDefault) || function (mod) { | ||
@@ -6,3 +22,3 @@ return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.verify = exports.sign = exports.getStarkKey = exports.getKeyPair = exports.genKeyPair = exports.ec = void 0; | ||
exports.verify = exports.sign = exports.getKeyPairFromPublicKey = exports.getStarkKey = exports.getKeyPair = exports.genKeyPair = exports.ec = void 0; | ||
var elliptic_1 = require("elliptic"); | ||
@@ -54,2 +70,13 @@ var hash_js_1 = __importDefault(require("hash.js")); | ||
exports.getStarkKey = getStarkKey; | ||
/** | ||
* Takes a public key and casts it into `elliptic` KeyPair format. | ||
* | ||
* @param publicKey - public key which should get casted to a KeyPair | ||
* @returns keyPair with public key only, which can be used to verify signatures, but cant sign anything | ||
*/ | ||
function getKeyPairFromPublicKey(publicKey) { | ||
var publicKeyBn = (0, number_1.toBN)(publicKey); | ||
return exports.ec.keyFromPublic((0, encode_1.removeHexPrefix)((0, number_1.toHex)(publicKeyBn)), 'hex'); | ||
} | ||
exports.getKeyPairFromPublicKey = getKeyPairFromPublicKey; | ||
/* | ||
@@ -71,5 +98,10 @@ Signs a message using the provided key. | ||
(0, number_1.assertInRange)(w, constants_1.ONE, (0, number_1.toBN)((0, encode_1.addHexPrefix)(constants_1.MAX_ECDSA_VAL)), 'w'); | ||
return msgSignature; | ||
return [r, s]; | ||
} | ||
exports.sign = sign; | ||
function chunkArray(arr, n) { | ||
return Array(Math.ceil(arr.length / n)) | ||
.fill('') | ||
.map(function (_, i) { return arr.slice(i * n, i * n + n); }); | ||
} | ||
/* | ||
@@ -82,12 +114,19 @@ Verifies a message using the provided key. | ||
function verify(keyPair, msgHash, sig) { | ||
var keyPairArray = Array.isArray(keyPair) ? keyPair : [keyPair]; | ||
var msgHashBN = (0, number_1.toBN)((0, encode_1.addHexPrefix)(msgHash)); | ||
(0, minimalistic_assert_1.default)(sig.length % 2 === 0, 'Signature must be an array of length dividable by 2'); | ||
(0, number_1.assertInRange)(msgHashBN, constants_1.ZERO, (0, number_1.toBN)((0, encode_1.addHexPrefix)(constants_1.MAX_ECDSA_VAL)), 'msgHash'); | ||
var r = sig.r, s = sig.s; | ||
var w = s.invm(exports.ec.n); | ||
// Verify signature has valid length. | ||
(0, number_1.assertInRange)(r, constants_1.ONE, (0, number_1.toBN)((0, encode_1.addHexPrefix)(constants_1.MAX_ECDSA_VAL)), 'r'); | ||
(0, number_1.assertInRange)(s, constants_1.ONE, (0, number_1.toBN)((0, encode_1.addHexPrefix)(constants_1.EC_ORDER)), 's'); | ||
(0, number_1.assertInRange)(w, constants_1.ONE, (0, number_1.toBN)((0, encode_1.addHexPrefix)(constants_1.MAX_ECDSA_VAL)), 'w'); | ||
return keyPair.verify(fixMessage(msgHash), sig); | ||
(0, minimalistic_assert_1.default)(keyPairArray.length === sig.length / 2, 'Signature and keyPair length must be equal'); | ||
return chunkArray(sig, 2).every(function (_a, i) { | ||
var _b; | ||
var _c = __read(_a, 2), r = _c[0], s = _c[1]; | ||
var rBN = (0, number_1.toBN)(r); | ||
var sBN = (0, number_1.toBN)(s); | ||
var w = sBN.invm(exports.ec.n); | ||
(0, number_1.assertInRange)(rBN, constants_1.ONE, (0, number_1.toBN)((0, encode_1.addHexPrefix)(constants_1.MAX_ECDSA_VAL)), 'r'); | ||
(0, number_1.assertInRange)(sBN, constants_1.ONE, (0, number_1.toBN)((0, encode_1.addHexPrefix)(constants_1.EC_ORDER)), 's'); | ||
(0, number_1.assertInRange)(w, constants_1.ONE, (0, number_1.toBN)((0, encode_1.addHexPrefix)(constants_1.MAX_ECDSA_VAL)), 'w'); | ||
return (_b = exports.ec.verify(fixMessage(msgHash), { r: rBN, s: sBN }, keyPairArray[i])) !== null && _b !== void 0 ? _b : false; | ||
}); | ||
} | ||
exports.verify = verify; |
@@ -1,3 +0,2 @@ | ||
import { CompressedProgram, Program } from '../types'; | ||
import { BigNumberish } from './number'; | ||
import { CompressedProgram, Program, Signature } from '../types'; | ||
/** | ||
@@ -21,2 +20,2 @@ * Function to compress compiled cairo program | ||
export declare function makeAddress(input: string): string; | ||
export declare function formatSignature(sig?: [BigNumberish, BigNumberish]): [string, string] | []; | ||
export declare function formatSignature(sig?: Signature): string[]; |
@@ -20,1 +20,2 @@ /** | ||
export * as shortString from './utils/shortString'; | ||
export * as typedData from './utils/typedData'; |
@@ -47,3 +47,4 @@ 'use strict'; | ||
Object.defineProperty(exports, '__esModule', { value: true }); | ||
exports.shortString = | ||
exports.typedData = | ||
exports.shortString = | ||
exports.uint256 = | ||
@@ -77,1 +78,2 @@ exports.ec = | ||
exports.shortString = __importStar(require('./utils/shortString')); | ||
exports.typedData = __importStar(require('./utils/typedData')); |
{ | ||
"name": "starknet", | ||
"version": "2.4.0", | ||
"version": "2.5.0", | ||
"description": "JavaScript library for StarkNet", | ||
@@ -71,2 +71,3 @@ "main": "dist/index.js", | ||
"pako": "^2.0.4", | ||
"superstruct": "^0.15.3", | ||
"url-join": "^4.0.1" | ||
@@ -73,0 +74,0 @@ }, |
@@ -11,2 +11,3 @@ import { | ||
GetTransactionStatusResponse, | ||
Signature, | ||
Transaction, | ||
@@ -134,3 +135,3 @@ } from '../types'; | ||
calldata?: string[], | ||
signature?: [BigNumberish, BigNumberish] | ||
signature?: Signature | ||
): Promise<AddTransactionResponse>; | ||
@@ -137,0 +138,0 @@ waitForTx(txHash: BigNumberish, retryInterval?: number): Promise<void>; |
@@ -520,17 +520,17 @@ 'use strict'; | ||
onchain = false; | ||
_a.label = 1; | ||
return [4 /*yield*/, wait(retryInterval)]; | ||
case 1: | ||
if (!!onchain) return [3 /*break*/, 4]; | ||
_a.sent(); | ||
_a.label = 2; | ||
case 2: | ||
if (!!onchain) return [3 /*break*/, 5]; | ||
// eslint-disable-next-line no-await-in-loop | ||
return [4 /*yield*/, wait(retryInterval)]; | ||
case 2: | ||
case 3: | ||
// eslint-disable-next-line no-await-in-loop | ||
_a.sent(); | ||
return [4 /*yield*/, this.getTransactionStatus(txHash)]; | ||
case 3: | ||
case 4: | ||
res = _a.sent(); | ||
if ( | ||
res.tx_status === 'ACCEPTED_ONCHAIN' || | ||
(res.tx_status === 'PENDING' && res.block_hash !== 'pending') // This is needed as of today. In the future there will be a different status for pending transactions. | ||
) { | ||
if (res.tx_status === 'ACCEPTED_ON_L1' || res.tx_status === 'ACCEPTED_ON_L2') { | ||
onchain = true; | ||
@@ -542,4 +542,4 @@ } else if (res.tx_status === 'REJECTED') { | ||
} | ||
return [3 /*break*/, 1]; | ||
case 4: | ||
return [3 /*break*/, 2]; | ||
case 5: | ||
return [2 /*return*/]; | ||
@@ -546,0 +546,0 @@ } |
@@ -11,2 +11,3 @@ import type { | ||
GetTransactionStatusResponse, | ||
Signature, | ||
Transaction, | ||
@@ -121,5 +122,5 @@ } from '../types'; | ||
calldata?: string[], | ||
signature?: [BigNumberish, BigNumberish] | ||
signature?: Signature | ||
): Promise<AddTransactionResponse>; | ||
abstract waitForTx(txHash: BigNumberish, retryInterval?: number): Promise<void>; | ||
} |
import { Provider } from '../provider'; | ||
import { AddTransactionResponse, KeyPair, Transaction } from '../types'; | ||
import { AddTransactionResponse, KeyPair, Signature, Transaction } from '../types'; | ||
import { TypedData } from '../utils/typedData'; | ||
import { SignerInterface } from './interface'; | ||
@@ -17,2 +18,18 @@ export declare class Signer extends Provider implements SignerInterface { | ||
addTransaction(transaction: Transaction): Promise<AddTransactionResponse>; | ||
/** | ||
* Sign an JSON object with the starknet private key and return the signature | ||
* | ||
* @param json - JSON object to be signed | ||
* @returns the signature of the JSON object | ||
* @throws {Error} if the JSON object is not a valid JSON | ||
*/ | ||
signMessage(typedData: TypedData): Promise<Signature>; | ||
/** | ||
* Hash a JSON object with pederson hash and return the hash | ||
* | ||
* @param json - JSON object to be hashed | ||
* @returns the hash of the JSON object | ||
* @throws {Error} if the JSON object is not a valid JSON | ||
*/ | ||
hashMessage(typedData: TypedData): Promise<string>; | ||
} |
@@ -206,2 +206,3 @@ 'use strict'; | ||
var stark_1 = require('../utils/stark'); | ||
var typedData_1 = require('../utils/typedData'); | ||
var Signer = /** @class */ (function (_super) { | ||
@@ -225,5 +226,5 @@ __extends(Signer, _super); | ||
return __awaiter(this, void 0, void 0, function () { | ||
var nonceBn, result, calldataDecimal, msgHash, _a, r, s; | ||
return __generator(this, function (_b) { | ||
switch (_b.label) { | ||
var nonceBn, result, calldataDecimal, msgHash, signature; | ||
return __generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: | ||
@@ -246,5 +247,5 @@ if (transaction.type === 'DEPLOY') | ||
case 2: | ||
result = _b.sent().result; | ||
result = _a.sent().result; | ||
nonceBn = (0, number_1.toBN)(result[0]); | ||
_b.label = 3; | ||
_a.label = 3; | ||
case 3: | ||
@@ -263,3 +264,3 @@ calldataDecimal = (transaction.calldata || []).map(function (x) { | ||
); | ||
(_a = (0, ellipticCurve_1.sign)(this.keyPair, msgHash)), (r = _a.r), (s = _a.s); | ||
signature = (0, ellipticCurve_1.sign)(this.keyPair, msgHash); | ||
return [ | ||
@@ -286,3 +287,3 @@ 2 /*return*/, | ||
contract_address: this.address, | ||
signature: [r, s], | ||
signature: signature, | ||
}), | ||
@@ -294,4 +295,40 @@ ]; | ||
}; | ||
/** | ||
* Sign an JSON object with the starknet private key and return the signature | ||
* | ||
* @param json - JSON object to be signed | ||
* @returns the signature of the JSON object | ||
* @throws {Error} if the JSON object is not a valid JSON | ||
*/ | ||
Signer.prototype.signMessage = function (typedData) { | ||
return __awaiter(this, void 0, void 0, function () { | ||
var _a, _b; | ||
return __generator(this, function (_c) { | ||
switch (_c.label) { | ||
case 0: | ||
_a = ellipticCurve_1.sign; | ||
_b = [this.keyPair]; | ||
return [4 /*yield*/, this.hashMessage(typedData)]; | ||
case 1: | ||
return [2 /*return*/, _a.apply(void 0, _b.concat([_c.sent()]))]; | ||
} | ||
}); | ||
}); | ||
}; | ||
/** | ||
* Hash a JSON object with pederson hash and return the hash | ||
* | ||
* @param json - JSON object to be hashed | ||
* @returns the hash of the JSON object | ||
* @throws {Error} if the JSON object is not a valid JSON | ||
*/ | ||
Signer.prototype.hashMessage = function (typedData) { | ||
return __awaiter(this, void 0, void 0, function () { | ||
return __generator(this, function (_a) { | ||
return [2 /*return*/, (0, typedData_1.getMessageHash)(typedData, this.address)]; | ||
}); | ||
}); | ||
}; | ||
return Signer; | ||
})(provider_1.Provider); | ||
exports.Signer = Signer; |
import { Provider } from '../provider'; | ||
import { AddTransactionResponse, Transaction } from '../types'; | ||
import { AddTransactionResponse, Signature, Transaction } from '../types'; | ||
import { TypedData } from '../utils/typedData/types'; | ||
export declare abstract class SignerInterface extends Provider { | ||
@@ -14,2 +15,20 @@ abstract address: string; | ||
abstract addTransaction(transaction: Transaction): Promise<AddTransactionResponse>; | ||
/** | ||
* Sign an JSON object for off-chain usage with the starknet private key and return the signature | ||
* This adds a message prefix so it cant be interchanged with transactions | ||
* | ||
* @param json - JSON object to be signed | ||
* @returns the signature of the JSON object | ||
* @throws {Error} if the JSON object is not a valid JSON | ||
*/ | ||
abstract signMessage(typedData: TypedData): Promise<Signature>; | ||
/** | ||
* Hash a JSON object with pederson hash and return the hash | ||
* This adds a message prefix so it cant be interchanged with transactions | ||
* | ||
* @param json - JSON object to be hashed | ||
* @returns the hash of the JSON object | ||
* @throws {Error} if the JSON object is not a valid JSON | ||
*/ | ||
abstract hashMessage(typedData: TypedData): Promise<string>; | ||
} |
@@ -5,3 +5,3 @@ import BN from 'bn.js'; | ||
import { Provider, defaultProvider } from './provider'; | ||
import { Abi, AbiEntry, FunctionAbi, StructAbi } from './types'; | ||
import { Abi, AbiEntry, FunctionAbi, Signature, StructAbi } from './types'; | ||
import { BigNumberish, toBN } from './utils/number'; | ||
@@ -150,3 +150,3 @@ import { getSelectorFromName } from './utils/stark'; | ||
public invoke(method: string, args: Args = {}, signature?: [BigNumberish, BigNumberish]) { | ||
public invoke(method: string, args: Args = {}, signature?: Signature) { | ||
// ensure contract is connected | ||
@@ -153,0 +153,0 @@ assert(this.connectedTo !== null, 'contract isnt connected to an address'); |
@@ -21,1 +21,2 @@ /** | ||
export * as shortString from './utils/shortString'; | ||
export * as typedData from './utils/typedData'; |
@@ -14,2 +14,3 @@ import axios from 'axios'; | ||
GetTransactionStatusResponse, | ||
Signature, | ||
Transaction, | ||
@@ -269,3 +270,3 @@ } from '../types'; | ||
calldata?: string[], | ||
signature?: [BigNumberish, BigNumberish] | ||
signature?: Signature | ||
): Promise<AddTransactionResponse> { | ||
@@ -283,2 +284,4 @@ return this.addTransaction({ | ||
let onchain = false; | ||
await wait(retryInterval); | ||
while (!onchain) { | ||
@@ -290,6 +293,3 @@ // eslint-disable-next-line no-await-in-loop | ||
if ( | ||
res.tx_status === 'ACCEPTED_ONCHAIN' || | ||
(res.tx_status === 'PENDING' && res.block_hash !== 'pending') // This is needed as of today. In the future there will be a different status for pending transactions. | ||
) { | ||
if (res.tx_status === 'ACCEPTED_ON_L1' || res.tx_status === 'ACCEPTED_ON_L2') { | ||
onchain = true; | ||
@@ -296,0 +296,0 @@ } else if (res.tx_status === 'REJECTED') { |
@@ -11,2 +11,3 @@ import type { | ||
GetTransactionStatusResponse, | ||
Signature, | ||
Transaction, | ||
@@ -139,3 +140,3 @@ } from '../types'; | ||
calldata?: string[], | ||
signature?: [BigNumberish, BigNumberish] | ||
signature?: Signature | ||
): Promise<AddTransactionResponse>; | ||
@@ -142,0 +143,0 @@ |
import assert from 'minimalistic-assert'; | ||
import { Provider } from '../provider'; | ||
import { AddTransactionResponse, KeyPair, Transaction } from '../types'; | ||
import { AddTransactionResponse, KeyPair, Signature, Transaction } from '../types'; | ||
import { sign } from '../utils/ellipticCurve'; | ||
@@ -10,2 +10,3 @@ import { addHexPrefix } from '../utils/encode'; | ||
import { getSelectorFromName } from '../utils/stark'; | ||
import { TypedData, getMessageHash } from '../utils/typedData'; | ||
import { SignerInterface } from './interface'; | ||
@@ -63,3 +64,3 @@ | ||
const { r, s } = sign(this.keyPair, msgHash); | ||
const signature = sign(this.keyPair, msgHash); | ||
@@ -77,5 +78,27 @@ return super.addTransaction({ | ||
contract_address: this.address, | ||
signature: [r, s], | ||
signature, | ||
}); | ||
} | ||
/** | ||
* Sign an JSON object with the starknet private key and return the signature | ||
* | ||
* @param json - JSON object to be signed | ||
* @returns the signature of the JSON object | ||
* @throws {Error} if the JSON object is not a valid JSON | ||
*/ | ||
public async signMessage(typedData: TypedData): Promise<Signature> { | ||
return sign(this.keyPair, await this.hashMessage(typedData)); | ||
} | ||
/** | ||
* Hash a JSON object with pederson hash and return the hash | ||
* | ||
* @param json - JSON object to be hashed | ||
* @returns the hash of the JSON object | ||
* @throws {Error} if the JSON object is not a valid JSON | ||
*/ | ||
public async hashMessage(typedData: TypedData): Promise<string> { | ||
return getMessageHash(typedData, this.address); | ||
} | ||
} |
import { Provider } from '../provider'; | ||
import { AddTransactionResponse, Transaction } from '../types'; | ||
import { AddTransactionResponse, Signature, Transaction } from '../types'; | ||
import { TypedData } from '../utils/typedData/types'; | ||
@@ -17,2 +18,22 @@ export abstract class SignerInterface extends Provider { | ||
): Promise<AddTransactionResponse>; | ||
/** | ||
* Sign an JSON object for off-chain usage with the starknet private key and return the signature | ||
* This adds a message prefix so it cant be interchanged with transactions | ||
* | ||
* @param json - JSON object to be signed | ||
* @returns the signature of the JSON object | ||
* @throws {Error} if the JSON object is not a valid JSON | ||
*/ | ||
public abstract signMessage(typedData: TypedData): Promise<Signature>; | ||
/** | ||
* Hash a JSON object with pederson hash and return the hash | ||
* This adds a message prefix so it cant be interchanged with transactions | ||
* | ||
* @param json - JSON object to be hashed | ||
* @returns the hash of the JSON object | ||
* @throws {Error} if the JSON object is not a valid JSON | ||
*/ | ||
public abstract hashMessage(typedData: TypedData): Promise<string>; | ||
} |
@@ -6,3 +6,3 @@ import type { ec as EC } from 'elliptic'; | ||
export type KeyPair = EC.KeyPair; | ||
export type Signature = EC.Signature; | ||
export type Signature = BigNumberish[]; | ||
@@ -14,3 +14,9 @@ export type GetContractAddressesResponse = { | ||
export type Status = 'NOT_RECEIVED' | 'RECEIVED' | 'PENDING' | 'REJECTED' | 'ACCEPTED_ONCHAIN'; | ||
export type Status = | ||
| 'NOT_RECEIVED' | ||
| 'RECEIVED' | ||
| 'PENDING' | ||
| 'ACCEPTED_ON_L2' | ||
| 'ACCEPTED_ON_L1' | ||
| 'REJECTED'; | ||
export type TransactionStatus = 'TRANSACTION_RECEIVED'; | ||
@@ -62,3 +68,3 @@ export type Type = 'DEPLOY' | 'INVOKE_FUNCTION'; | ||
contract_address: string; | ||
signature?: [BigNumberish, BigNumberish]; | ||
signature?: Signature; | ||
entry_point_type?: EntryPointType; | ||
@@ -65,0 +71,0 @@ entry_point_selector: string; |
@@ -56,2 +56,13 @@ import { ec as EC, curves } from 'elliptic'; | ||
/** | ||
* Takes a public key and casts it into `elliptic` KeyPair format. | ||
* | ||
* @param publicKey - public key which should get casted to a KeyPair | ||
* @returns keyPair with public key only, which can be used to verify signatures, but cant sign anything | ||
*/ | ||
export function getKeyPairFromPublicKey(publicKey: BigNumberish): KeyPair { | ||
const publicKeyBn = toBN(publicKey); | ||
return ec.keyFromPublic(removeHexPrefix(toHex(publicKeyBn)), 'hex'); | ||
} | ||
/* | ||
@@ -73,5 +84,11 @@ Signs a message using the provided key. | ||
assertInRange(w, ONE, toBN(addHexPrefix(MAX_ECDSA_VAL)), 'w'); | ||
return msgSignature; | ||
return [r, s]; | ||
} | ||
function chunkArray(arr: any[], n: number): any[][] { | ||
return Array(Math.ceil(arr.length / n)) | ||
.fill('') | ||
.map((_, i) => arr.slice(i * n, i * n + n)); | ||
} | ||
/* | ||
@@ -83,13 +100,18 @@ Verifies a message using the provided key. | ||
*/ | ||
export function verify(keyPair: KeyPair, msgHash: string, sig: Signature): boolean { | ||
export function verify(keyPair: KeyPair | KeyPair[], msgHash: string, sig: Signature): boolean { | ||
const keyPairArray = Array.isArray(keyPair) ? keyPair : [keyPair]; | ||
const msgHashBN = toBN(addHexPrefix(msgHash)); | ||
assert(sig.length % 2 === 0, 'Signature must be an array of length dividable by 2'); | ||
assertInRange(msgHashBN, ZERO, toBN(addHexPrefix(MAX_ECDSA_VAL)), 'msgHash'); | ||
const { r, s } = sig; | ||
const w = s.invm(ec.n!); | ||
// Verify signature has valid length. | ||
assertInRange(r, ONE, toBN(addHexPrefix(MAX_ECDSA_VAL)), 'r'); | ||
assertInRange(s, ONE, toBN(addHexPrefix(EC_ORDER)), 's'); | ||
assertInRange(w, ONE, toBN(addHexPrefix(MAX_ECDSA_VAL)), 'w'); | ||
assert(keyPairArray.length === sig.length / 2, 'Signature and keyPair length must be equal'); | ||
return keyPair.verify(fixMessage(msgHash), sig); | ||
return chunkArray(sig, 2).every(([r, s], i) => { | ||
const rBN = toBN(r); | ||
const sBN = toBN(s); | ||
const w = sBN.invm((ec as any).n); | ||
assertInRange(rBN, ONE, toBN(addHexPrefix(MAX_ECDSA_VAL)), 'r'); | ||
assertInRange(sBN, ONE, toBN(addHexPrefix(EC_ORDER)), 's'); | ||
assertInRange(w, ONE, toBN(addHexPrefix(MAX_ECDSA_VAL)), 'w'); | ||
return ec.verify(fixMessage(msgHash), { r: rBN, s: sBN }, keyPairArray[i]) ?? false; | ||
}); | ||
} |
import { gzip } from 'pako'; | ||
import { CompressedProgram, Program } from '../types'; | ||
import { CompressedProgram, Program, Signature } from '../types'; | ||
import { genKeyPair, getStarkKey } from './ellipticCurve'; | ||
@@ -8,3 +8,3 @@ import { addHexPrefix, btoaUniversal } from './encode'; | ||
import { stringify } from './json'; | ||
import { BigNumberish, toBN, toHex } from './number'; | ||
import { toBN, toHex } from './number'; | ||
@@ -45,6 +45,6 @@ /** | ||
export function formatSignature(sig?: [BigNumberish, BigNumberish]): [string, string] | [] { | ||
export function formatSignature(sig?: Signature): string[] { | ||
if (!sig) return []; | ||
try { | ||
return sig.map((x) => toBN(x)).map((x) => x.toString()) as [string, string]; | ||
return sig.map((x) => toBN(x)).map((x) => x.toString()); | ||
} catch (e) { | ||
@@ -51,0 +51,0 @@ return []; |
@@ -5,3 +5,3 @@ import type { ec as EC } from 'elliptic'; | ||
export declare type KeyPair = EC.KeyPair; | ||
export declare type Signature = EC.Signature; | ||
export declare type Signature = BigNumberish[]; | ||
export declare type GetContractAddressesResponse = { | ||
@@ -15,4 +15,5 @@ Starknet: string; | ||
| 'PENDING' | ||
| 'REJECTED' | ||
| 'ACCEPTED_ONCHAIN'; | ||
| 'ACCEPTED_ON_L2' | ||
| 'ACCEPTED_ON_L1' | ||
| 'REJECTED'; | ||
export declare type TransactionStatus = 'TRANSACTION_RECEIVED'; | ||
@@ -60,3 +61,3 @@ export declare type Type = 'DEPLOY' | 'INVOKE_FUNCTION'; | ||
contract_address: string; | ||
signature?: [BigNumberish, BigNumberish]; | ||
signature?: Signature; | ||
entry_point_type?: EntryPointType; | ||
@@ -63,0 +64,0 @@ entry_point_selector: string; |
@@ -9,3 +9,14 @@ import { ec as EC } from 'elliptic'; | ||
export declare function getStarkKey(keyPair: KeyPair): string; | ||
/** | ||
* Takes a public key and casts it into `elliptic` KeyPair format. | ||
* | ||
* @param publicKey - public key which should get casted to a KeyPair | ||
* @returns keyPair with public key only, which can be used to verify signatures, but cant sign anything | ||
*/ | ||
export declare function getKeyPairFromPublicKey(publicKey: BigNumberish): KeyPair; | ||
export declare function sign(keyPair: KeyPair, msgHash: string): Signature; | ||
export declare function verify(keyPair: KeyPair, msgHash: string, sig: Signature): boolean; | ||
export declare function verify( | ||
keyPair: KeyPair | KeyPair[], | ||
msgHash: string, | ||
sig: Signature | ||
): boolean; |
'use strict'; | ||
var __read = | ||
(this && this.__read) || | ||
function (o, n) { | ||
var m = typeof Symbol === 'function' && o[Symbol.iterator]; | ||
if (!m) return o; | ||
var i = m.call(o), | ||
r, | ||
ar = [], | ||
e; | ||
try { | ||
while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); | ||
} catch (error) { | ||
e = { error: error }; | ||
} finally { | ||
try { | ||
if (r && !r.done && (m = i['return'])) m.call(i); | ||
} finally { | ||
if (e) throw e.error; | ||
} | ||
} | ||
return ar; | ||
}; | ||
var __importDefault = | ||
@@ -10,2 +32,3 @@ (this && this.__importDefault) || | ||
exports.sign = | ||
exports.getKeyPairFromPublicKey = | ||
exports.getStarkKey = | ||
@@ -66,2 +89,16 @@ exports.getKeyPair = | ||
exports.getStarkKey = getStarkKey; | ||
/** | ||
* Takes a public key and casts it into `elliptic` KeyPair format. | ||
* | ||
* @param publicKey - public key which should get casted to a KeyPair | ||
* @returns keyPair with public key only, which can be used to verify signatures, but cant sign anything | ||
*/ | ||
function getKeyPairFromPublicKey(publicKey) { | ||
var publicKeyBn = (0, number_1.toBN)(publicKey); | ||
return exports.ec.keyFromPublic( | ||
(0, encode_1.removeHexPrefix)((0, number_1.toHex)(publicKeyBn)), | ||
'hex' | ||
); | ||
} | ||
exports.getKeyPairFromPublicKey = getKeyPairFromPublicKey; | ||
/* | ||
@@ -104,5 +141,12 @@ Signs a message using the provided key. | ||
); | ||
return msgSignature; | ||
return [r, s]; | ||
} | ||
exports.sign = sign; | ||
function chunkArray(arr, n) { | ||
return Array(Math.ceil(arr.length / n)) | ||
.fill('') | ||
.map(function (_, i) { | ||
return arr.slice(i * n, i * n + n); | ||
}); | ||
} | ||
/* | ||
@@ -115,3 +159,8 @@ Verifies a message using the provided key. | ||
function verify(keyPair, msgHash, sig) { | ||
var keyPairArray = Array.isArray(keyPair) ? keyPair : [keyPair]; | ||
var msgHashBN = (0, number_1.toBN)((0, encode_1.addHexPrefix)(msgHash)); | ||
(0, minimalistic_assert_1.default)( | ||
sig.length % 2 === 0, | ||
'Signature must be an array of length dividable by 2' | ||
); | ||
(0, number_1.assertInRange)( | ||
@@ -123,26 +172,26 @@ msgHashBN, | ||
); | ||
var r = sig.r, | ||
s = sig.s; | ||
var w = s.invm(exports.ec.n); | ||
// Verify signature has valid length. | ||
(0, number_1.assertInRange)( | ||
r, | ||
constants_1.ONE, | ||
(0, number_1.toBN)((0, encode_1.addHexPrefix)(constants_1.MAX_ECDSA_VAL)), | ||
'r' | ||
(0, minimalistic_assert_1.default)( | ||
keyPairArray.length === sig.length / 2, | ||
'Signature and keyPair length must be equal' | ||
); | ||
(0, number_1.assertInRange)( | ||
s, | ||
constants_1.ONE, | ||
(0, number_1.toBN)((0, encode_1.addHexPrefix)(constants_1.EC_ORDER)), | ||
's' | ||
); | ||
(0, number_1.assertInRange)( | ||
w, | ||
constants_1.ONE, | ||
(0, number_1.toBN)((0, encode_1.addHexPrefix)(constants_1.MAX_ECDSA_VAL)), | ||
'w' | ||
); | ||
return keyPair.verify(fixMessage(msgHash), sig); | ||
return chunkArray(sig, 2).every(function (_a, i) { | ||
var _b; | ||
var _c = __read(_a, 2), | ||
r = _c[0], | ||
s = _c[1]; | ||
var rBN = (0, number_1.toBN)(r); | ||
var sBN = (0, number_1.toBN)(s); | ||
var w = sBN.invm(exports.ec.n); | ||
(0, | ||
number_1.assertInRange)(rBN, constants_1.ONE, (0, number_1.toBN)((0, encode_1.addHexPrefix)(constants_1.MAX_ECDSA_VAL)), 'r'); | ||
(0, | ||
number_1.assertInRange)(sBN, constants_1.ONE, (0, number_1.toBN)((0, encode_1.addHexPrefix)(constants_1.EC_ORDER)), 's'); | ||
(0, | ||
number_1.assertInRange)(w, constants_1.ONE, (0, number_1.toBN)((0, encode_1.addHexPrefix)(constants_1.MAX_ECDSA_VAL)), 'w'); | ||
return (_b = exports.ec.verify(fixMessage(msgHash), { r: rBN, s: sBN }, keyPairArray[i])) !== | ||
null && _b !== void 0 | ||
? _b | ||
: false; | ||
}); | ||
} | ||
exports.verify = verify; |
@@ -1,3 +0,2 @@ | ||
import { CompressedProgram, Program } from '../types'; | ||
import { BigNumberish } from './number'; | ||
import { CompressedProgram, Program, Signature } from '../types'; | ||
/** | ||
@@ -21,2 +20,2 @@ * Function to compress compiled cairo program | ||
export declare function makeAddress(input: string): string; | ||
export declare function formatSignature(sig?: [BigNumberish, BigNumberish]): [string, string] | []; | ||
export declare function formatSignature(sig?: Signature): string[]; |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
5429732
139
141119
10
+ Addedsuperstruct@^0.15.3
+ Addedsuperstruct@0.15.5(transitive)