starknet 4.0.1 → 4.3.0

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.
Files changed (59) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/__tests__/defaultProvider.test.ts +5 -0
  3. package/__tests__/sequencerProvider.test.ts +31 -1
  4. package/__tests__/utils/address.test.ts +2 -2
  5. package/dist/index.d.ts +1 -0
  6. package/dist/index.js +1 -0
  7. package/dist/provider/default.d.ts +2 -1
  8. package/dist/provider/default.js +7 -0
  9. package/dist/provider/interface.d.ts +2 -1
  10. package/dist/provider/rpc.d.ts +1 -0
  11. package/dist/provider/rpc.js +13 -0
  12. package/dist/provider/sequencer.d.ts +1 -0
  13. package/dist/provider/sequencer.js +13 -5
  14. package/dist/types/api/sequencer.d.ts +4 -4
  15. package/dist/types/provider.d.ts +3 -0
  16. package/dist/utils/address.js +1 -1
  17. package/dist/utils/hash.d.ts +1 -0
  18. package/dist/utils/hash.js +8 -1
  19. package/dist/utils/responseParser/rpc.d.ts +1 -0
  20. package/dist/utils/responseParser/rpc.js +3 -0
  21. package/dist/utils/responseParser/sequencer.d.ts +1 -0
  22. package/dist/utils/responseParser/sequencer.js +3 -0
  23. package/dist/utils/url.d.ts +7 -0
  24. package/dist/utils/url.js +49 -0
  25. package/index.d.ts +1 -0
  26. package/index.js +1 -0
  27. package/package.json +2 -4
  28. package/provider/default.d.ts +2 -1
  29. package/provider/default.js +7 -0
  30. package/provider/interface.d.ts +2 -1
  31. package/provider/rpc.d.ts +1 -0
  32. package/provider/rpc.js +13 -0
  33. package/provider/sequencer.d.ts +1 -0
  34. package/provider/sequencer.js +13 -5
  35. package/src/index.ts +1 -0
  36. package/src/provider/default.ts +8 -0
  37. package/src/provider/interface.ts +6 -0
  38. package/src/provider/rpc.ts +9 -0
  39. package/src/provider/sequencer.ts +17 -3
  40. package/src/types/api/sequencer.ts +5 -5
  41. package/src/types/provider.ts +5 -0
  42. package/src/utils/address.ts +2 -2
  43. package/src/utils/hash.ts +8 -1
  44. package/src/utils/responseParser/rpc.ts +4 -0
  45. package/src/utils/responseParser/sequencer.ts +4 -0
  46. package/src/utils/url.ts +53 -0
  47. package/types/api/sequencer.d.ts +4 -4
  48. package/types/provider.d.ts +3 -0
  49. package/utils/address.js +1 -1
  50. package/utils/hash.d.ts +1 -0
  51. package/utils/hash.js +8 -1
  52. package/utils/responseParser/rpc.d.ts +1 -0
  53. package/utils/responseParser/rpc.js +3 -0
  54. package/utils/responseParser/sequencer.d.ts +1 -0
  55. package/utils/responseParser/sequencer.js +3 -0
  56. package/utils/url.d.ts +7 -0
  57. package/utils/url.js +49 -0
  58. package/www/docs/API/provider.md +80 -23
  59. package/www/guides/account.md +24 -2
@@ -10,6 +10,7 @@ import type {
10
10
  DeployContractResponse,
11
11
  EstimateFeeResponse,
12
12
  GetBlockResponse,
13
+ GetCodeResponse,
13
14
  GetTransactionReceiptResponse,
14
15
  GetTransactionResponse,
15
16
  Invocation,
@@ -42,6 +43,11 @@ export abstract class ProviderInterface {
42
43
  */
43
44
  public abstract getBlock(blockIdentifier: BlockIdentifier): Promise<GetBlockResponse>;
44
45
 
46
+ public abstract getCode(
47
+ contractAddress: string,
48
+ blockIdentifier?: BlockIdentifier
49
+ ): Promise<GetCodeResponse>;
50
+
45
51
  /**
46
52
  * Gets the contract class of the deployed contract.
47
53
  *
@@ -213,6 +213,15 @@ export class RpcProvider implements ProviderInterface {
213
213
  return this.responseParser.parseCallContractResponse(result);
214
214
  }
215
215
 
216
+ public async getCode(
217
+ contractAddress: string,
218
+ _blockIdentifier?: BlockIdentifier
219
+ ): Promise<RPC.GetCodeResponse> {
220
+ const result = await this.fetchEndpoint('starknet_getCode', [contractAddress]);
221
+
222
+ return this.responseParser.parseGetCodeResponse(result);
223
+ }
224
+
216
225
  public async waitForTransaction(txHash: BigNumberish, retryInterval: number = 8000) {
217
226
  let onchain = false;
218
227
  let retries = 100;
@@ -31,6 +31,7 @@ import { BigNumberish, bigNumberishArrayToDecimalStringArray, toBN, toHex } from
31
31
  import { parseContract, wait } from '../utils/provider';
32
32
  import { SequencerAPIResponseParser } from '../utils/responseParser/sequencer';
33
33
  import { randomAddress } from '../utils/stark';
34
+ import { buildUrl } from '../utils/url';
34
35
  import { GatewayError, HttpError } from './errors';
35
36
  import { ProviderInterface } from './interface';
36
37
  import { BlockIdentifier, getFormattedBlockIdentifier } from './utils';
@@ -74,9 +75,13 @@ export class SequencerProvider implements ProviderInterface {
74
75
  this.gatewayUrl = urljoin(this.baseUrl, 'gateway');
75
76
  } else {
76
77
  this.baseUrl = optionsOrProvider.baseUrl;
77
- this.feederGatewayUrl =
78
- optionsOrProvider.feederGatewayUrl ?? urljoin(this.baseUrl, 'feeder_gateway');
79
- this.gatewayUrl = optionsOrProvider.gatewayUrl ?? urljoin(this.baseUrl, 'gateway');
78
+ this.feederGatewayUrl = buildUrl(
79
+ this.baseUrl,
80
+ 'feeder_gateway',
81
+ optionsOrProvider.feederGatewayUrl
82
+ );
83
+ this.gatewayUrl = buildUrl(this.baseUrl, 'gateway', optionsOrProvider.gatewayUrl);
84
+
80
85
  this.chainId =
81
86
  optionsOrProvider.chainId ??
82
87
  SequencerProvider.getChainIdFromBaseUrl(optionsOrProvider.baseUrl);
@@ -323,6 +328,15 @@ export class SequencerProvider implements ProviderInterface {
323
328
  ).then(this.responseParser.parseFeeEstimateResponse);
324
329
  }
325
330
 
331
+ public async getCode(
332
+ contractAddress: string,
333
+ blockIdentifier: BlockIdentifier = 'pending'
334
+ ): Promise<Sequencer.GetCodeResponse> {
335
+ return this.fetchEndpoint('get_code', { contractAddress, blockIdentifier }).then(
336
+ this.responseParser.parseGetCodeResponse
337
+ );
338
+ }
339
+
326
340
  public async waitForTransaction(txHash: BigNumberish, retryInterval: number = 8000) {
327
341
  let onchain = false;
328
342
 
@@ -53,11 +53,6 @@ export type ExecutionResources = {
53
53
  n_memory_holes: number;
54
54
  };
55
55
 
56
- export type GetCodeResponse = {
57
- bytecode: string[];
58
- abi: Abi;
59
- };
60
-
61
56
  export type GetTransactionTraceResponse = {
62
57
  function_invocation: {
63
58
  caller_address: string;
@@ -116,6 +111,11 @@ export namespace Sequencer {
116
111
  class_hash?: string;
117
112
  };
118
113
 
114
+ export type GetCodeResponse = {
115
+ bytecode: string[];
116
+ abi: Abi;
117
+ };
118
+
119
119
  export interface InvokeFunctionTransactionResponse extends InvokeFunctionTransaction {
120
120
  transaction_hash: string;
121
121
  }
@@ -16,6 +16,11 @@ export interface GetBlockResponse {
16
16
  starknet_version?: string;
17
17
  }
18
18
 
19
+ export interface GetCodeResponse {
20
+ bytecode: string[];
21
+ // abi: string; // is not consistent between rpc and sequencer (is it?), therefore not included in the provider interface
22
+ }
23
+
19
24
  export type GetTransactionResponse = InvokeTransactionResponse & DeclareTransactionResponse;
20
25
 
21
26
  export interface CommonTransactionResponse {
@@ -3,7 +3,7 @@ import { arrayify } from '@ethersproject/bytes';
3
3
 
4
4
  import { MASK_251, ZERO } from '../constants';
5
5
  import { addHexPrefix, removeHexPrefix } from './encode';
6
- import { pedersen } from './hash';
6
+ import { keccakBn } from './hash';
7
7
  import { BigNumberish, assertInRange, toBN, toHex } from './number';
8
8
 
9
9
  export function addAddressPadding(address: BigNumberish): string {
@@ -25,7 +25,7 @@ export function validateAndParseAddress(address: BigNumberish): string {
25
25
  // from https://github.com/ethers-io/ethers.js/blob/fc1e006575d59792fa97b4efb9ea2f8cca1944cf/packages/address/src.ts/index.ts#L12
26
26
  export function getChecksumAddress(address: BigNumberish): string {
27
27
  const chars = removeHexPrefix(validateAndParseAddress(address)).toLowerCase().split('');
28
- const hashed = arrayify(pedersen([0, address]), { hexPad: 'left' }); // as the hash will be 251 bits (63 chars) we need to pad it to 64 chars without changing the number value ("left")
28
+ const hashed = arrayify(keccakBn(address), { hexPad: 'left' }); // in case the hash is 251 bits (63 chars) we need to pad it to 64 chars without changing the number value ("left")
29
29
 
30
30
  for (let i = 0; i < chars.length; i += 2) {
31
31
  if (hashed[i >> 1] >> 4 >= 8) {
package/src/utils/hash.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import BN from 'bn.js';
2
2
  import { keccak256 } from 'ethereum-cryptography/keccak';
3
+ import { hexToBytes } from 'ethereum-cryptography/utils';
3
4
  import assert from 'minimalistic-assert';
4
5
 
5
6
  import {
@@ -13,12 +14,18 @@ import {
13
14
  } from '../constants';
14
15
  import { RawCalldata } from '../types/lib';
15
16
  import { ec } from './ellipticCurve';
16
- import { addHexPrefix, buf2hex, utf8ToArray } from './encode';
17
+ import { addHexPrefix, buf2hex, removeHexPrefix, utf8ToArray } from './encode';
17
18
  import { BigNumberish, toBN, toFelt, toHex } from './number';
18
19
 
19
20
  export const transactionVersion = 0;
20
21
  export const feeTransactionVersion = toBN(2).pow(toBN(128)).add(toBN(transactionVersion));
21
22
 
23
+ export function keccakBn(value: BigNumberish): string {
24
+ const hexWithoutPrefix = removeHexPrefix(toHex(toBN(value)));
25
+ const evenHex = hexWithoutPrefix.length % 2 === 0 ? hexWithoutPrefix : `0${hexWithoutPrefix}`;
26
+ return addHexPrefix(buf2hex(keccak256(hexToBytes(evenHex))));
27
+ }
28
+
22
29
  function keccakHex(value: string): string {
23
30
  return addHexPrefix(buf2hex(keccak256(utf8ToArray(value))));
24
31
  }
@@ -57,6 +57,10 @@ export class RPCResponseParser extends ResponseParser {
57
57
  };
58
58
  }
59
59
 
60
+ public parseGetCodeResponse(res: RPC.GetCodeResponse): RPC.GetCodeResponse {
61
+ return res;
62
+ }
63
+
60
64
  public parseFeeEstimateResponse(res: RPC.EstimateFeeResponse): EstimateFeeResponse {
61
65
  return {
62
66
  overall_fee: toBN(res.overall_fee),
@@ -70,6 +70,10 @@ export class SequencerAPIResponseParser extends ResponseParser {
70
70
  };
71
71
  }
72
72
 
73
+ public parseGetCodeResponse(res: Sequencer.GetCodeResponse): Sequencer.GetCodeResponse {
74
+ return res;
75
+ }
76
+
73
77
  public parseFeeEstimateResponse(res: Sequencer.EstimateFeeResponse): EstimateFeeResponse {
74
78
  if ('overall_fee' in res) {
75
79
  let gasInfo = {};
@@ -0,0 +1,53 @@
1
+ import urljoin from 'url-join';
2
+
3
+ /**
4
+ * Inspired from https://github.com/segmentio/is-url
5
+ */
6
+
7
+ /**
8
+ * RegExps.
9
+ * A URL must match #1 and then at least one of #2/#3.
10
+ * Use two levels of REs to avoid REDOS.
11
+ */
12
+ const protocolAndDomainRE = /^(?:\w+:)?\/\/(\S+)$/;
13
+
14
+ const localhostDomainRE = /^localhost[:?\d]*(?:[^:?\d]\S*)?$/;
15
+ const nonLocalhostDomainRE = /^[^\s.]+\.\S{2,}$/;
16
+
17
+ /**
18
+ * Loosely validate a URL `string`.
19
+ * @param {String} s
20
+ * @return {Boolean}
21
+ */
22
+ export function isUrl(s?: string): boolean {
23
+ if (!s) {
24
+ return false;
25
+ }
26
+
27
+ if (typeof s !== 'string') {
28
+ return false;
29
+ }
30
+
31
+ const match = s.match(protocolAndDomainRE);
32
+ if (!match) {
33
+ return false;
34
+ }
35
+
36
+ const everythingAfterProtocol = match[1];
37
+ if (!everythingAfterProtocol) {
38
+ return false;
39
+ }
40
+
41
+ if (
42
+ localhostDomainRE.test(everythingAfterProtocol) ||
43
+ nonLocalhostDomainRE.test(everythingAfterProtocol)
44
+ ) {
45
+ return true;
46
+ }
47
+
48
+ return false;
49
+ }
50
+
51
+ export function buildUrl(baseUrl: string, defaultPath: string, urlOrPath?: string) {
52
+ return isUrl(urlOrPath) ? urlOrPath! : urljoin(baseUrl, urlOrPath ?? defaultPath);
53
+ }
@@ -39,10 +39,6 @@ export declare type ExecutionResources = {
39
39
  };
40
40
  n_memory_holes: number;
41
41
  };
42
- export declare type GetCodeResponse = {
43
- bytecode: string[];
44
- abi: Abi;
45
- };
46
42
  export declare type GetTransactionTraceResponse = {
47
43
  function_invocation: {
48
44
  caller_address: string;
@@ -97,6 +93,10 @@ export declare namespace Sequencer {
97
93
  address?: string;
98
94
  class_hash?: string;
99
95
  };
96
+ type GetCodeResponse = {
97
+ bytecode: string[];
98
+ abi: Abi;
99
+ };
100
100
  interface InvokeFunctionTransactionResponse extends InvokeFunctionTransaction {
101
101
  transaction_hash: string;
102
102
  }
@@ -13,6 +13,9 @@ export interface GetBlockResponse {
13
13
  transactions: Array<string>;
14
14
  starknet_version?: string;
15
15
  }
16
+ export interface GetCodeResponse {
17
+ bytecode: string[];
18
+ }
16
19
  export declare type GetTransactionResponse = InvokeTransactionResponse & DeclareTransactionResponse;
17
20
  export interface CommonTransactionResponse {
18
21
  transaction_hash?: string;
package/utils/address.js CHANGED
@@ -23,7 +23,7 @@ exports.validateAndParseAddress = validateAndParseAddress;
23
23
  // from https://github.com/ethers-io/ethers.js/blob/fc1e006575d59792fa97b4efb9ea2f8cca1944cf/packages/address/src.ts/index.ts#L12
24
24
  function getChecksumAddress(address) {
25
25
  var chars = (0, encode_1.removeHexPrefix)(validateAndParseAddress(address)).toLowerCase().split('');
26
- var hashed = (0, bytes_1.arrayify)((0, hash_1.pedersen)([0, address]), { hexPad: 'left' }); // as the hash will be 251 bits (63 chars) we need to pad it to 64 chars without changing the number value ("left")
26
+ var hashed = (0, bytes_1.arrayify)((0, hash_1.keccakBn)(address), { hexPad: 'left' }); // in case the hash is 251 bits (63 chars) we need to pad it to 64 chars without changing the number value ("left")
27
27
  for (var i = 0; i < chars.length; i += 2) {
28
28
  if (hashed[i >> 1] >> 4 >= 8) {
29
29
  chars[i] = chars[i].toUpperCase();
package/utils/hash.d.ts CHANGED
@@ -4,6 +4,7 @@ import { RawCalldata } from '../types/lib';
4
4
  import { BigNumberish } from './number';
5
5
  export declare const transactionVersion = 0;
6
6
  export declare const feeTransactionVersion: BN;
7
+ export declare function keccakBn(value: BigNumberish): string;
7
8
  /**
8
9
  * Function to get the starknet keccak hash from a string
9
10
  *
package/utils/hash.js CHANGED
@@ -28,8 +28,9 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
28
28
  return (mod && mod.__esModule) ? mod : { "default": mod };
29
29
  };
30
30
  Object.defineProperty(exports, "__esModule", { value: true });
31
- exports.calculateContractAddressFromHash = exports.calculcateTransactionHash = exports.calculateDeployTransactionHash = exports.calculateTransactionHashCommon = exports.computeHashOnElements = exports.pedersen = exports.getSelectorFromName = exports.starknetKeccak = exports.feeTransactionVersion = exports.transactionVersion = void 0;
31
+ exports.calculateContractAddressFromHash = exports.calculcateTransactionHash = exports.calculateDeployTransactionHash = exports.calculateTransactionHashCommon = exports.computeHashOnElements = exports.pedersen = exports.getSelectorFromName = exports.starknetKeccak = exports.keccakBn = exports.feeTransactionVersion = exports.transactionVersion = void 0;
32
32
  var keccak_1 = require("ethereum-cryptography/keccak");
33
+ var utils_1 = require("ethereum-cryptography/utils");
33
34
  var minimalistic_assert_1 = __importDefault(require("minimalistic-assert"));
34
35
  var constants_1 = require("../constants");
35
36
  var ellipticCurve_1 = require("./ellipticCurve");
@@ -37,6 +38,12 @@ var encode_1 = require("./encode");
37
38
  var number_1 = require("./number");
38
39
  exports.transactionVersion = 0;
39
40
  exports.feeTransactionVersion = (0, number_1.toBN)(2).pow((0, number_1.toBN)(128)).add((0, number_1.toBN)(exports.transactionVersion));
41
+ function keccakBn(value) {
42
+ var hexWithoutPrefix = (0, encode_1.removeHexPrefix)((0, number_1.toHex)((0, number_1.toBN)(value)));
43
+ var evenHex = hexWithoutPrefix.length % 2 === 0 ? hexWithoutPrefix : "0".concat(hexWithoutPrefix);
44
+ return (0, encode_1.addHexPrefix)((0, encode_1.buf2hex)((0, keccak_1.keccak256)((0, utils_1.hexToBytes)(evenHex))));
45
+ }
46
+ exports.keccakBn = keccakBn;
40
47
  function keccakHex(value) {
41
48
  return (0, encode_1.addHexPrefix)((0, encode_1.buf2hex)((0, keccak_1.keccak256)((0, encode_1.utf8ToArray)(value))));
42
49
  }
@@ -5,6 +5,7 @@ export declare class RPCResponseParser extends ResponseParser {
5
5
  parseGetBlockResponse(res: RPC.GetBlockResponse): GetBlockResponse;
6
6
  parseGetTransactionResponse(res: RPC.GetTransactionResponse): GetTransactionResponse;
7
7
  parseGetTransactionReceiptResponse(res: RPC.GetTransactionReceiptResponse): GetTransactionReceiptResponse;
8
+ parseGetCodeResponse(res: RPC.GetCodeResponse): RPC.GetCodeResponse;
8
9
  parseFeeEstimateResponse(res: RPC.EstimateFeeResponse): EstimateFeeResponse;
9
10
  parseCallContractResponse(res: Array<string>): CallContractResponse;
10
11
  parseInvokeFunctionResponse(res: RPC.AddTransactionResponse): InvokeFunctionResponse;
@@ -62,6 +62,9 @@ var RPCResponseParser = /** @class */ (function (_super) {
62
62
  events: res.events,
63
63
  };
64
64
  };
65
+ RPCResponseParser.prototype.parseGetCodeResponse = function (res) {
66
+ return res;
67
+ };
65
68
  RPCResponseParser.prototype.parseFeeEstimateResponse = function (res) {
66
69
  return {
67
70
  overall_fee: (0, number_1.toBN)(res.overall_fee),
@@ -5,6 +5,7 @@ export declare class SequencerAPIResponseParser extends ResponseParser {
5
5
  parseGetBlockResponse(res: Sequencer.GetBlockResponse): GetBlockResponse;
6
6
  parseGetTransactionResponse(res: Sequencer.GetTransactionResponse): GetTransactionResponse;
7
7
  parseGetTransactionReceiptResponse(res: Sequencer.TransactionReceiptResponse): GetTransactionReceiptResponse;
8
+ parseGetCodeResponse(res: Sequencer.GetCodeResponse): Sequencer.GetCodeResponse;
8
9
  parseFeeEstimateResponse(res: Sequencer.EstimateFeeResponse): EstimateFeeResponse;
9
10
  parseCallContractResponse(res: Sequencer.CallContractResponse): CallContractResponse;
10
11
  parseInvokeFunctionResponse(res: Sequencer.AddTransactionResponse): InvokeFunctionResponse;
@@ -79,6 +79,9 @@ var SequencerAPIResponseParser = /** @class */ (function (_super) {
79
79
  l1_origin_message: undefined,
80
80
  };
81
81
  };
82
+ SequencerAPIResponseParser.prototype.parseGetCodeResponse = function (res) {
83
+ return res;
84
+ };
82
85
  SequencerAPIResponseParser.prototype.parseFeeEstimateResponse = function (res) {
83
86
  if ('overall_fee' in res) {
84
87
  var gasInfo = {};
package/utils/url.d.ts ADDED
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Loosely validate a URL `string`.
3
+ * @param {String} s
4
+ * @return {Boolean}
5
+ */
6
+ export declare function isUrl(s?: string): boolean;
7
+ export declare function buildUrl(baseUrl: string, defaultPath: string, urlOrPath?: string): string;
package/utils/url.js ADDED
@@ -0,0 +1,49 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.buildUrl = exports.isUrl = void 0;
7
+ var url_join_1 = __importDefault(require("url-join"));
8
+ /**
9
+ * Inspired from https://github.com/segmentio/is-url
10
+ */
11
+ /**
12
+ * RegExps.
13
+ * A URL must match #1 and then at least one of #2/#3.
14
+ * Use two levels of REs to avoid REDOS.
15
+ */
16
+ var protocolAndDomainRE = /^(?:\w+:)?\/\/(\S+)$/;
17
+ var localhostDomainRE = /^localhost[:?\d]*(?:[^:?\d]\S*)?$/;
18
+ var nonLocalhostDomainRE = /^[^\s.]+\.\S{2,}$/;
19
+ /**
20
+ * Loosely validate a URL `string`.
21
+ * @param {String} s
22
+ * @return {Boolean}
23
+ */
24
+ function isUrl(s) {
25
+ if (!s) {
26
+ return false;
27
+ }
28
+ if (typeof s !== 'string') {
29
+ return false;
30
+ }
31
+ var match = s.match(protocolAndDomainRE);
32
+ if (!match) {
33
+ return false;
34
+ }
35
+ var everythingAfterProtocol = match[1];
36
+ if (!everythingAfterProtocol) {
37
+ return false;
38
+ }
39
+ if (localhostDomainRE.test(everythingAfterProtocol) ||
40
+ nonLocalhostDomainRE.test(everythingAfterProtocol)) {
41
+ return true;
42
+ }
43
+ return false;
44
+ }
45
+ exports.isUrl = isUrl;
46
+ function buildUrl(baseUrl, defaultPath, urlOrPath) {
47
+ return isUrl(urlOrPath) ? urlOrPath : (0, url_join_1.default)(baseUrl, urlOrPath !== null && urlOrPath !== void 0 ? urlOrPath : defaultPath);
48
+ }
49
+ exports.buildUrl = buildUrl;