# Seller

{% hint style="info" %}
The seller entity MUST be created before any seller actions can be performed.
{% endhint %}

A seller can create offers and participate in exchanges. The seller is a complex role with admin, assistant and treasury. Initially, all roles must be the same Ethereum address; however, the admin can later change them to other addresses.

When a seller is created, a default collection is initialised and all rNFTs belonging to their offers are issued on that collection. If the seller wants to organise offers and issue rNFTs on different collections, the protocol enables them to create as many collections as needed.

The sellers manage royalty recipients on two levels - the admin defines a list of allowed recipients, and the assistant then decides which royalty recipients are added to which offer.

Seller's funds management is described in [deposit-and-withdraw](https://docs.bosonprotocol.io/using-the-protocol/dacp-tools/finances/deposit-and-withdraw "mention").

### TypeScript SDK

{% tabs fullWidth="true" %}
{% tab title="Create seller" %}
Creates a new seller. The assistant, admin and treasury must be the caller's address.

```typescript
const sellerAddress = signerWallet.address;

await coreSDK.createSeller({
  assistant: sellerAddress,
  admin: sellerAddress,
  treasury: sellerAddress,
  contractUri: "http://contract.uri",
  royaltyPercentage: "0",
  authTokenId: "0",
  authTokenType: 0,
  metadataUri: "http://metadata.uri",
});
```

{% endtab %}

{% tab title="Update seller" %}
Change the admin, the assistant, the treasury, or the authentication method. Except for the treasury, all other actions require confirmation from the new address.

```typescript
const newAssistantWallet = Wallet.createRandom(ethereumProvider);
const sellerId = "12";
const seller = await coreSDK.getSellerById(sellerId);

// Start the update with the seller's wallet
await coreSDK.updateSellerAndOptIn({
   ...seller,
   {assistant: newAssistantWallet.address}
})

// Finalize the update with the assistant's wallet 
const assistantCoreSDK = CoreSDK.fromDefaultConfig({
   envName,
   configId,
   web3Lib: new EthersAdapter(provider, newAssistantWallet),
});

await assistantCoreSDK.optInToSellerUpdate({
   id: sellerId,
   fieldsToUpdate: {assistant: true}
})
```

{% endtab %}

{% tab title="Create new collection" %}
Create a new rNFT collection (ERC721 contract) which can be assigned to new offers.

```typescript
await coreSDK.createNewCollection({
      contractUri: "http://newCollection.uri",
      collectionId: "customCollectionID"
});
```

To get the new collection index, one can query

```typescript
const collections = await coreSDK.getOfferCollections({
      offerCollectionsFilter: {
        sellerId: seller.id
      }
});

const latestCollectionId = collections[collections.length - 1].collectionIndex
```

{% endtab %}

{% tab title="Manage royalty recipients" %}
Add, update or remove royalty recipients allowlist.

#### Add

```typescript
const recipients = [
  "recipientAddress1",
  "recipientAddress2"
];
const recipientsPercentage = ["200", "300"];
    
await coreSDK.addRoyaltyRecipients(
      seller.id,
      recipients.map((wallet, index) => {
        return {
          wallet,
          minRoyaltyPercentage: recipientsPercentage[index]
        };
      })
);
```

#### Update

```typescript
const newRecipients = [
  "recipientAddress1", // address unchanged
  "recipientAddress3"
];
const recipientsPercentage = ["400", "100"];
const recipientIndices = [1,2];

await coreSDK.updateRoyaltyRecipients(
      seller.id,
      recipientIndices,
      recipients_2.map((wallet, index) => {
        return {
          wallet,
          minRoyaltyPercentage: recipientsPercentage_2[index]
        };
      })
);
```

#### Remove

```typescript
const recipient2Index = 2;
await coreSDK.removeRoyaltyRecipients(seller.id, [
      recipient2Index
]);
```

{% endtab %}
{% endtabs %}

### Solidity

{% tabs fullWidth="true" %}
{% tab title="Create seller" %}
Creates a new seller. The assistant, admin and treasury must be the caller's address.

```solidity
IBosonProtocol bosonProtocol = IBosonSellerHandler(_bosonProtocolAddress);
BosonTypes.Seller memory seller = BosonTypes.Seller({
     id: 0,
     assistant: address(this),
     admin: address(this),
     clerk: address(0),
     treasury: payable(address(this)),
     active: true,
     metadataUri: ""
});

BosonTypes.AuthToken memory authToken;
BosonTypes.VoucherInitValues memory voucherInitValues;

bosonProtocol.createSeller(seller, authToken, voucherInitValues);
```

{% endtab %}

{% tab title="Update seller" %}
Change the admin, the assistant, the treasury, or the authentication method. Except for the treasury, all other actions require confirmation from the new address.

```solidity
IBosonAccountHandler bosonProtocol = IBosonAccountHandler(_bosonProtocolAddress);

BosonTypes.Seller memory seller = BosonTypes.Seller({
    id: 1, // existing seller ID
    assistant: newAssistantAddress,
    admin: newAdminAddress, 
    clerk: address(0), // deprecated, always zero
    treasury: payable(newTreasuryAddress),
    active: true,
    metadataUri: "https://ipfs.io/ipfs/updated-metadata"
});

BosonTypes.AuthToken memory authToken = BosonTypes.AuthToken({
    tokenId: 0,
    tokenType: BosonTypes.AuthTokenType.None
});

bosonProtocol.updateSeller(seller, authToken);

// For admin/assistant updates, new addresses must opt in:
// bosonProtocol.optInToSellerUpdate(sellerId, fieldsToUpdate);
```

{% endtab %}

{% tab title="Create new collection" %}
Create a new rNFT collection (ERC721 contract) which can be assigned to new offers.

```solidity
IBosonAccountHandler bosonProtocol = IBosonAccountHandler(_bosonProtocolAddress);

BosonTypes.VoucherInitValues memory voucherInitValues = BosonTypes.VoucherInitValues({
    contractURI: "https://ipfs.io/ipfs/collection-metadata",
    royaltyPercentage: 250, // 2.5%
    collectionSalt: keccak256(abi.encodePacked(block.timestamp, "unique-collection"))
});

bosonProtocol.createNewCollection("external-collection-id", voucherInitValues);
```

{% hint style="warning" %}
In case of admin address being updated it is a good practice also the salt to be updated. If not there exists a possibility that the old admin will try to create the vouchers with matching addresses on other chains.
{% endhint %}

```solidity
IBosonAccountHandler bosonProtocol = IBosonAccountHandler(_bosonProtocolAddress);

uint256 sellerId = 1;
bytes32 newSalt = keccak256(abi.encodePacked(block.timestamp, "new-unique-salt"));

bosonProtocol.updateSellerSalt(sellerId, newSalt);
```

{% endtab %}

{% tab title="Manage royalty recipients" %}
Add, update or remove royalty recipients allowlist.

```solidity
IBosonAccountHandler bosonProtocol = IBosonAccountHandler(_bosonProtocolAddress);
uint256 sellerId = 1;

// Add royalty recipients
BosonTypes.RoyaltyRecipientInfo[] memory royaltyRecipients = new BosonTypes.RoyaltyRecipientInfo[](2);
royaltyRecipients[0] = BosonTypes.RoyaltyRecipientInfo({
    wallet: payable(recipientAddress1),
    minRoyaltyPercentage: 100 // 1%
});
royaltyRecipients[1] = BosonTypes.RoyaltyRecipientInfo({
    wallet: payable(recipientAddress2),
    minRoyaltyPercentage: 150 // 1.5%
});

bosonProtocol.addRoyaltyRecipients(sellerId, royaltyRecipients);

// Update royalty recipients
uint256[] memory recipientIds = new uint256[](1);
recipientIds[0] = 0; // first recipient
royaltyRecipients[0].minRoyaltyPercentage = 200; // update to 2%

bosonProtocol.updateRoyaltyRecipients(sellerId, recipientIds, royaltyRecipients);

// Remove royalty recipients
uint256[] memory idsToRemove = new uint256[](1);
idsToRemove[0] = 1; // second recipient

bosonProtocol.removeRoyaltyRecipients(sellerId, idsToRemove);
```

{% endtab %}
{% endtabs %}
