Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: accepting addresses as string #1583

Merged
merged 13 commits into from
Dec 22, 2023
7 changes: 7 additions & 0 deletions .changeset/soft-falcons-agree.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"@fuel-ts/providers": minor
"@fuel-ts/wallet": minor
"@fuel-ts/wallet-manager": minor
---

accepting string as address instead of only AbstractAddress
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ describe(__filename, () => {
});

const tx2 = await predicate.transfer(
receiverWallet.address,
receiverWallet.address.toB256(),
amountToPredicate - 1000,
BaseAssetId,
{
Expand Down
2 changes: 1 addition & 1 deletion packages/fuel-gauge/src/contract.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -969,7 +969,7 @@ describe('Contract', () => {

const amountToContract = 100;

const tx = await wallet.transferToContract(contract.id, amountToContract, asset, {
const tx = await wallet.transferToContract(contract.id.toB256(), amountToContract, asset, {
gasPrice,
gasLimit: 10_000,
});
Expand Down
33 changes: 18 additions & 15 deletions packages/providers/src/provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -843,16 +843,17 @@ export default class Provider {
}

async getResourcesForTransaction(
owner: AbstractAddress,
owner: string | AbstractAddress,
danielbate marked this conversation as resolved.
Show resolved Hide resolved
transactionRequestLike: TransactionRequestLike,
forwardingQuantities: CoinQuantity[] = []
) {
const ownerAddress = Address.fromAddressOrString(owner);
const transactionRequest = transactionRequestify(clone(transactionRequestLike));
const transactionCost = await this.getTransactionCost(transactionRequest, forwardingQuantities);

// Add the required resources to the transaction from the owner
transactionRequest.addResources(
await this.getResourcesToSpend(owner, transactionCost.requiredQuantities)
await this.getResourcesToSpend(ownerAddress, transactionCost.requiredQuantities)
);
// Refetch transaction costs with the new resources
// TODO: we could find a way to avoid fetch estimatePredicates again, by returning the transaction or
Expand All @@ -864,7 +865,7 @@ export default class Provider {
transactionRequest,
forwardingQuantities
);
const resources = await this.getResourcesToSpend(owner, requiredQuantities);
const resources = await this.getResourcesToSpend(ownerAddress, requiredQuantities);

return {
resources,
Expand All @@ -878,16 +879,17 @@ export default class Provider {
*/
async getCoins(
/** The address to get coins for */
owner: AbstractAddress,
owner: string | AbstractAddress,
/** The asset ID of coins to get */
assetId?: BytesLike,
/** Pagination arguments */
paginationArgs?: CursorPaginationArgs
): Promise<Coin[]> {
const ownerAddress = Address.fromAddressOrString(owner);
const result = await this.operations.getCoins({
first: 10,
...paginationArgs,
filter: { owner: owner.toB256(), assetId: assetId && hexlify(assetId) },
filter: { owner: ownerAddress.toB256(), assetId: assetId && hexlify(assetId) },
});

const coins = result.coins.edges.map((edge) => edge.node);
Expand All @@ -913,12 +915,13 @@ export default class Provider {
*/
async getResourcesToSpend(
/** The address to get coins for */
owner: AbstractAddress,
owner: string | AbstractAddress,
/** The quantities to get */
quantities: CoinQuantityLike[],
/** IDs of excluded resources from the selection. */
excludedIds?: ExcludeResourcesOption
): Promise<Resource[]> {
const ownerAddress = Address.fromAddressOrString(owner);
const excludeInput = {
messages: excludedIds?.messages?.map((nonce) => hexlify(nonce)) || [],
utxos: excludedIds?.utxos?.map((id) => hexlify(id)) || [],
Expand All @@ -931,7 +934,7 @@ export default class Provider {
excludeInput.utxos = Array.from(uniqueUtxos);
}
const coinsQuery = {
owner: owner.toB256(),
owner: ownerAddress.toB256(),
queryPerAsset: quantities
.map(coinQuantityfy)
.map(({ assetId, amount, max: maxPerAsset }) => ({
Expand Down Expand Up @@ -1108,12 +1111,12 @@ export default class Provider {
*/
async getContractBalance(
/** The contract ID to get the balance for */
contractId: AbstractAddress,
contractId: string | AbstractAddress,
/** The asset ID of coins to get */
assetId: BytesLike
): Promise<BN> {
const { contractBalance } = await this.operations.getContractBalance({
contract: contractId.toB256(),
contract: Address.fromAddressOrString(contractId).toB256(),
asset: hexlify(assetId),
});
return bn(contractBalance.amount, 10);
Expand All @@ -1128,12 +1131,12 @@ export default class Provider {
*/
async getBalance(
/** The address to get coins for */
owner: AbstractAddress,
owner: string | AbstractAddress,
/** The asset ID of coins to get */
assetId: BytesLike
): Promise<BN> {
const { balance } = await this.operations.getBalance({
owner: owner.toB256(),
owner: Address.fromAddressOrString(owner).toB256(),
assetId: hexlify(assetId),
});
return bn(balance.amount, 10);
Expand All @@ -1148,14 +1151,14 @@ export default class Provider {
*/
async getBalances(
/** The address to get coins for */
owner: AbstractAddress,
owner: string | AbstractAddress,
/** Pagination arguments */
paginationArgs?: CursorPaginationArgs
): Promise<CoinQuantity[]> {
const result = await this.operations.getBalances({
first: 10,
...paginationArgs,
filter: { owner: owner.toB256() },
filter: { owner: Address.fromAddressOrString(owner).toB256() },
});

const balances = result.balances.edges.map((edge) => edge.node);
Expand All @@ -1175,14 +1178,14 @@ export default class Provider {
*/
async getMessages(
/** The address to get message from */
address: AbstractAddress,
address: string | AbstractAddress,
/** Pagination arguments */
paginationArgs?: CursorPaginationArgs
): Promise<Message[]> {
const result = await this.operations.getMessages({
first: 10,
...paginationArgs,
owner: address.toB256(),
owner: Address.fromAddressOrString(address).toB256(),
});

const messages = result.messages.edges.map((edge) => edge.node);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -226,8 +226,9 @@ export abstract class BaseTransactionRequest implements BaseTransactionRequestLi
* @param address - The address to get the coin input witness index for.
* @param signature - The signature to update the witness with.
*/
updateWitnessByOwner(address: AbstractAddress, signature: BytesLike) {
const witnessIndex = this.getCoinInputWitnessIndexByOwner(address);
updateWitnessByOwner(address: string | AbstractAddress, signature: BytesLike) {
const ownerAddress = Address.fromAddressOrString(address);
const witnessIndex = this.getCoinInputWitnessIndexByOwner(ownerAddress);
if (typeof witnessIndex === 'number') {
this.updateWitness(witnessIndex, signature);
}
Expand Down
22 changes: 22 additions & 0 deletions packages/providers/test/provider.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1096,4 +1096,26 @@ describe('Provider', () => {
expect(usedFee.eq(0)).not.toBeTruthy();
expect(minFee.eq(0)).not.toBeTruthy();
});

it('should accept string addresses in methods that require an address', async () => {
const provider = await Provider.create(FUEL_NETWORK_URL);

const b256Str = Address.fromRandom().toB256();

const methodCalls = [
() => provider.getBalance(b256Str, BaseAssetId),
() => provider.getCoins(b256Str),
() => provider.getResourcesForTransaction(b256Str, new ScriptTransactionRequest()),
() => provider.getResourcesToSpend(b256Str, []),
() => provider.getContractBalance(b256Str, BaseAssetId),
() => provider.getBalances(b256Str),
() => provider.getMessages(b256Str),
];

const promises = methodCalls.map(async (call) => {
await expect(call()).resolves.toBeTruthy();
});

await Promise.all(promises);
});
});
4 changes: 3 additions & 1 deletion packages/wallet-manager/src/vaults/mnemonic-vault.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@ describe('MnemonicVault', () => {

vault.addAccount();

expect(vault.getWallet(wallet.address).publicKey).toBe(walletManagerSpec.account_0.publicKey);
expect(vault.getWallet(wallet.address.toString()).publicKey).toBe(
walletManagerSpec.account_0.publicKey
);
});

it('Check if accounts are been added correctly', async () => {
Expand Down
9 changes: 5 additions & 4 deletions packages/wallet-manager/src/vaults/mnemonic-vault.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { Address } from '@fuel-ts/address';
import { ErrorCode, FuelError } from '@fuel-ts/errors';
import type { AbstractAddress } from '@fuel-ts/interfaces';
import { Mnemonic } from '@fuel-ts/mnemonic';
Expand Down Expand Up @@ -82,17 +83,17 @@ export class MnemonicVault implements Vault<MnemonicVaultOptions> {
};
}

exportAccount(address: AbstractAddress): string {
exportAccount(address: string | AbstractAddress): string {
let numberOfAccounts = 0;

const ownerAddress = Address.fromAddressOrString(address);
// Look for the account that has the same address
do {
const wallet = Wallet.fromMnemonic(
this.#secret,
this.provider,
this.getDerivePath(numberOfAccounts)
);
if (wallet.address.equals(address)) {
if (wallet.address.equals(ownerAddress)) {
return wallet.privateKey;
}
numberOfAccounts += 1;
Expand All @@ -104,7 +105,7 @@ export class MnemonicVault implements Vault<MnemonicVaultOptions> {
);
}

getWallet(address: AbstractAddress): WalletUnlocked {
getWallet(address: string | AbstractAddress): WalletUnlocked {
const privateKey = this.exportAccount(address);
return Wallet.fromPrivateKey(privateKey, this.provider);
}
Expand Down
8 changes: 5 additions & 3 deletions packages/wallet-manager/src/vaults/privatekey-vault.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { Address } from '@fuel-ts/address';
import { ErrorCode, FuelError } from '@fuel-ts/errors';
import type { AbstractAddress } from '@fuel-ts/interfaces';
import type { Provider } from '@fuel-ts/providers';
Expand Down Expand Up @@ -64,9 +65,10 @@ export class PrivateKeyVault implements Vault<PkVaultOptions> {
return this.getPublicAccount(wallet.privateKey);
}

exportAccount(address: AbstractAddress): string {
exportAccount(address: string | AbstractAddress): string {
const ownerAddress = Address.fromAddressOrString(address);
const privateKey = this.#privateKeys.find((pk) =>
Wallet.fromPrivateKey(pk, this.provider).address.equals(address)
Wallet.fromPrivateKey(pk, this.provider).address.equals(ownerAddress)
);

if (!privateKey) {
Expand All @@ -79,7 +81,7 @@ export class PrivateKeyVault implements Vault<PkVaultOptions> {
return privateKey;
}

getWallet(address: AbstractAddress): WalletUnlocked {
getWallet(address: string | AbstractAddress): WalletUnlocked {
const privateKey = this.exportAccount(address);
return Wallet.fromPrivateKey(privateKey, this.provider);
}
Expand Down
15 changes: 9 additions & 6 deletions packages/wallet-manager/src/wallet-manager.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { Address } from '@fuel-ts/address';
import type { Keystore } from '@fuel-ts/crypto';
import { encrypt, decrypt } from '@fuel-ts/crypto';
import { ErrorCode, FuelError } from '@fuel-ts/errors';
Expand Down Expand Up @@ -110,26 +111,28 @@ export class WalletManager extends EventEmitter {
/**
* Create a Wallet instance for the specific account
*/
getWallet(address: AbstractAddress): WalletUnlocked {
getWallet(address: string | AbstractAddress): WalletUnlocked {
const ownerAddress = Address.fromAddressOrString(address);
const vaultState = this.#vaults.find((vs) =>
vs.vault.getAccounts().find((a) => a.address.equals(address))
vs.vault.getAccounts().find((a) => a.address.equals(ownerAddress))
);
assert(vaultState, ERROR_MESSAGES.address_not_found);

return vaultState.vault.getWallet(address);
return vaultState.vault.getWallet(ownerAddress);
}

/**
* Export specific account privateKey
*/
exportPrivateKey(address: AbstractAddress) {
exportPrivateKey(address: string | AbstractAddress) {
const ownerAddress = Address.fromAddressOrString(address);
assert(!this.#isLocked, ERROR_MESSAGES.wallet_not_unlocked);
const vaultState = this.#vaults.find((vs) =>
vs.vault.getAccounts().find((a) => a.address.equals(address))
vs.vault.getAccounts().find((a) => a.address.equals(ownerAddress))
);
assert(vaultState, ERROR_MESSAGES.address_not_found);

return vaultState.vault.exportAccount(address);
return vaultState.vault.exportAccount(ownerAddress);
}

/**
Expand Down
18 changes: 10 additions & 8 deletions packages/wallet/src/account.ts
Original file line number Diff line number Diff line change
Expand Up @@ -292,7 +292,7 @@ export class Account extends AbstractAccount {
*/
async createTransfer(
/** Address of the destination */
destination: AbstractAddress,
destination: string | AbstractAddress,
/** Amount of coins */
amount: BigNumberish,
/** Asset ID of coins */
Expand All @@ -303,7 +303,7 @@ export class Account extends AbstractAccount {
const { minGasPrice } = this.provider.getGasConfig();
const params = { gasPrice: minGasPrice, ...txParams };
const request = new ScriptTransactionRequest(params);
request.addCoinOutput(destination, amount, assetId);
request.addCoinOutput(Address.fromAddressOrString(destination), amount, assetId);
const { maxFee, requiredQuantities } = await this.provider.getTransactionCost(request);
await this.fund(request, requiredQuantities, maxFee);
return request;
Expand All @@ -320,7 +320,7 @@ export class Account extends AbstractAccount {
*/
async transfer(
/** Address of the destination */
destination: AbstractAddress,
destination: string | AbstractAddress,
/** Amount of coins */
amount: BigNumberish,
/** Asset ID of coins */
Expand All @@ -343,21 +343,22 @@ export class Account extends AbstractAccount {
*/
async transferToContract(
/** Contract address */
contractId: AbstractAddress,
contractId: string | AbstractAddress,
/** Amount of coins */
amount: BigNumberish,
/** Asset ID of coins */
assetId: BytesLike = BaseAssetId,
/** Tx Params */
txParams: TxParamsType = {}
): Promise<TransactionResponse> {
const contractAddress = Address.fromAddressOrString(contractId);
const { minGasPrice } = this.provider.getGasConfig();
const params = { gasPrice: minGasPrice, ...txParams };

const script = await composeScriptForTransferringToContract();

const scriptData = formatScriptDataForTransferringToContract(
contractId.toB256(),
contractAddress.toB256(),
amount,
assetId
);
Expand All @@ -368,7 +369,7 @@ export class Account extends AbstractAccount {
scriptData,
});

request.addContractInputAndOutput(contractId);
request.addContractInputAndOutput(contractAddress);

const { maxFee, requiredQuantities } = await this.provider.getTransactionCost(request, [
{ amount: bn(amount), assetId: String(assetId) },
Expand All @@ -389,15 +390,16 @@ export class Account extends AbstractAccount {
*/
async withdrawToBaseLayer(
/** Address of the recipient on the base chain */
recipient: AbstractAddress,
recipient: string | AbstractAddress,
/** Amount of base asset */
amount: BigNumberish,
/** Tx Params */
txParams: TxParamsType = {}
): Promise<TransactionResponse> {
const recipientAddress = Address.fromAddressOrString(recipient);
// add recipient and amount to the transaction script code
const recipientDataArray = getBytesCopy(
'0x'.concat(recipient.toHexString().substring(2).padStart(64, '0'))
'0x'.concat(recipientAddress.toHexString().substring(2).padStart(64, '0'))
);
const amountDataArray = getBytesCopy(
'0x'.concat(bn(amount).toHex().substring(2).padStart(16, '0'))
Expand Down
Loading
Loading