# SDK Interfaces Design ## IMarketplace ### Administrator *(notes): Consider using get/set nomenclature* ## Proposal 1 ```typescript= interface IAccessControl { addAdministrator(address: string) : Promise<boolean>; removeAdministrator(address: string) : Promise<boolean>; addAssetManager(address : string) : Promise<boolean>; removeAssetManager(address : string) : Promise<boolean>; addAssetValidatorManager(address: string) : Promise<boolean>; removeAssetValidatorManager(address : string) : Promise<boolean>; addAssetValidator(address: string) : Promise<boolean>; removeAssetValidator(address: string) : Promise<boolean>; addFinance(address: string) : Promise<boolean>; removeFinance(address: string) : Promise<boolean>; addFinanceManager(address: string) : Promise<boolean>; removeFinanceManager(address: string) : Promise<boolean>; addAssetProviderManager(address: string) : Promise<boolean>; removeAssetProviderManager(address: string) : Promise<boolean>; addAssetProvider(address: string) : Promise<boolean>; removeAssetProvider(address: string) : Promise<boolean>; } /* Probably we need to segregate interfaces here */ interface IMarketplace { changeRequireWhitelistedAsset(requireWhitelistedAsset: boolean) : Promise<boolean>; changeRequireWhitelistedAssetProvider(requireWhitelistedAssetProvider: boolean) : Promise<boolean>; changeRequireAssetValidation(requireAssetValidation: boolean) : Promise<boolean>; changeRequireSaleStrategyPerAsset(requireSaleStrategyPerAsset: boolean) : Promise<boolean>; changeRequireSameCurrencyPerAsset(requireSameCurrencyPerAsset: boolean) : Promise<boolean>; changeRequireAssetSubdomain(requireAssetSubdomain: boolean) : Promise<boolean>; changeSupportsStakingSlashing(supportStakingSlashing: boolean) : Promise<boolean>; whitelistAsset(address: string) : Promise<boolean>; removeAssetFromWhitelist(address: string) : Promise<boolean>; isWhitelistedAsset(address: string) : Promise<boolean>; addAcceptedCurrency(tokenERC20: string) : Promise<boolean>; removeAcceptedCurrency(tokenERC20: string) : Promise<boolean>; withdraw(to: string, amount: number) : Promise<boolean>; addAssetValidatorDelegate(account: string) : Promise<boolean>; removeAssetValidatorDelegate(account: string) : Promise<boolean>; assignRnsDomain(rnsDomainNode: string) : Promise<boolean>; assignRnsNameGenerationStrategy(strategy: string) : Promise<boolean>; //registerAsset(Asset _asset) external; validatorDelegateApprovesAsset(assetValidator: string, asset: string) : Promise<boolean>; validatorDelegateRejectAsset(assetValidator: string, asset: string): Promise<boolean>; assignOrderIdGeneratorStrategy(strategy: string) : Promise<boolean>; sellAsset(asset: string) : Promise<boolean>; } interface IWhitelist { add(address: string) : Promise<boolean>; remove(address: string) : Promise<boolean>; isWhitelisted(address: string) : Promise<boolean>; } class AssetWhitelist implements IWhitelist { add(address: string) : Promise<boolean> { // TODO: Implement method return Promise.resolve(true); } remove(address: string) : Promise<boolean> { // TODO: Implement method return Promise.resolve(true); } isWhitelisted(address: string) : Promise<boolean> { // TODO: Implement method return Promise.resolve(true); } } /* We need to work on the interfaces for the smart contract interaction Hint: Look into legacy marketplace-ui contract - https://github.com/rsksmart/rif-marketplace-ui/blob/develop/src/contracts/wrappers/contract-base.ts */ abstract class ContractWrapper { } class MarketplaceContractWrapper extends ContractWrapper { constructor () { super(); } } /* TODO : Add Asset Validator Delegate | RNS parts */ class Marketplace implements IMarketplace, IAccessControl { private requireWhitelistedAsset! : boolean; private requireWhitelistedAssetProvider! : boolean; private requireAssetValidation! : boolean; private requireSaleStrategyPerAsset! : boolean; private requireSameCurrencyPerAsset! : boolean; private requireAssetSubdomain! : boolean; private supportsStakingSlashing! : boolean; private assetWhitelist! : AssetWhitelist; private contractWrapper : MarketplaceContractWrapper; // library to interact with contract (ethers); [index: string] : any; constructor (config: any = { }) { Object.keys(config).forEach((key: string) => this[key] = config[key]); this.assetWhitelist = new AssetWhitelist(); this.contractWrapper = new MarketplaceContractWrapper(); } whitelistAsset(address: string) : Promise<boolean> { return this.assetWhitelist.add(address); } removeAssetFromWhitelist(address: string) : Promise<boolean> { return this.assetWhitelist.remove(address); } isWhitelistedAsset(address: string) : Promise<boolean> { return this.assetWhitelist.isWhitelisted(address); } changeRequireWhitelistedAsset(requireWhitelistedAsset: boolean): Promise<boolean> { // TODO: Implement method return Promise.resolve(true); } changeRequireWhitelistedAssetProvider(requireWhitelistedAssetProvider: boolean) : Promise<boolean> { // TODO: Implement method return Promise.resolve(true); } changeRequireAssetValidation(requireAssetValidation: boolean) : Promise<boolean> { // TODO: Implement method return Promise.resolve(true); } changeRequireSaleStrategyPerAsset(requireSaleStrategyPerAsset: boolean) : Promise<boolean> { // TODO: Implement method return Promise.resolve(true); } changeRequireSameCurrencyPerAsset(requireSameCurrencyPerAsset: boolean) : Promise<boolean> { // TODO: Implement method return Promise.resolve(true); } changeRequireAssetSubdomain(requireAssetSubdomain: boolean) : Promise<boolean> { // TODO: Implement method return Promise.resolve(true); } changeSupportsStakingSlashing(supportStakingSlashing: boolean) : Promise<boolean> { // TODO: Implement method return Promise.resolve(true); } addAdministrator(address: string) : Promise<boolean> { // TODO: Implement method return Promise.resolve(true); } removeAdministrator(address: string) : Promise<boolean> { // TODO: Implement method return Promise.resolve(true); } addAssetManager(address : string) : Promise<boolean> { // TODO: Implement method return Promise.resolve(true); } removeAssetManager(address : string) : Promise<boolean> { // TODO: Implement method return Promise.resolve(true); } addAssetValidatorManager(address: string) : Promise<boolean> { // TODO: Implement method return Promise.resolve(true); } removeAssetValidatorManager(address : string) : Promise<boolean> { // TODO: Implement method return Promise.resolve(true); } addAssetValidator(address: string) : Promise<boolean> { // TODO: Implement method return Promise.resolve(true); } removeAssetValidator(address: string) : Promise<boolean> { // TODO: Implement method return Promise.resolve(true); } addFinance(address: string) : Promise<boolean> { // TODO: Implement method return Promise.resolve(true); } removeFinance(address: string) : Promise<boolean> { // TODO: Implement method return Promise.resolve(true); } addFinanceManager(address: string) : Promise<boolean> { // TODO: Implement method return Promise.resolve(true); } removeFinanceManager(address: string) : Promise<boolean> { // TODO: Implement method return Promise.resolve(true); } addAssetProviderManager(address: string) : Promise<boolean> { // TODO: Implement method return Promise.resolve(true); } removeAssetProviderManager(address: string) : Promise<boolean> { // TODO: Implement method return Promise.resolve(true); } addAssetProvider(address: string) : Promise<boolean> { // TODO: Implement method return Promise.resolve(true); } removeAssetProvider(address: string) : Promise<boolean> { // TODO: Implement method return Promise.resolve(true); } addAcceptedCurrency(tokenERC20: string) : Promise<boolean> { // TODO: Implement method return Promise.resolve(true); } removeAcceptedCurrency(tokenERC20: string) : Promise<boolean> { // TODO: Implement method return Promise.resolve(true); } withdraw(to: string, amount: number) : Promise<boolean> { // TODO: Implement method return Promise.resolve(true); } addAssetValidatorDelegate(account: string) : Promise<boolean> { // TODO: Implement method return Promise.resolve(true); } removeAssetValidatorDelegate(account: string) : Promise<boolean> { // TODO: Implement method return Promise.resolve(true); } assignRnsDomain(rnsDomainNode: string) : Promise<boolean> { // TODO: Implement method return Promise.resolve(true); } assignRnsNameGenerationStrategy(strategy: string) : Promise<boolean> { // TODO: Implement method return Promise.resolve(true); } //registerAsset(Asset _asset) external; validatorDelegateApprovesAsset(assetValidator: string, asset: string) : Promise<boolean> { // TODO: Implement method return Promise.resolve(true); } validatorDelegateRejectAsset(assetValidator: string, asset: string): Promise<boolean> { // TODO: Implement method return Promise.resolve(true); } assignOrderIdGeneratorStrategy(strategy: string) : Promise<boolean> { // TODO: Implement method return Promise.resolve(true); } sellAsset(asset: string) : Promise<boolean> { // TODO: Implement method return Promise.resolve(true); } } ``` ## Proposal 2 ```typescript= interface AccessControlable { addAdministrator(_adminAddr: string); removeAdministrator(_adminAddr: string); addAssetManager(_assetManagerAddr: string); removeAssetManager(_assetManagerAddr: string); addAssetValidatorManager(_assetValidatorManagerAddr: string) removeAssetValidatorManager(_assetValidatorAddr: string) ; addAssetValidator(_assetValidatorManagerAddr: string); removeAssetValidator(_assetValidatorAddr: string); addFinance(_financeAddr: string); removeFinance(_financeAddr: string); addFinanceManager(_financeManagerAddr: string); removeFinanceManager(_financeManagerAddr: string); addAssetProviderManager(_assetProviderManagerAddr: string); removeAssetProviderManager(_assetProviderManagerAddr: string); addAssetProvider(_assetProviderAddr: string); removeAssetProvider(_assetProviderAddr: string); } interface Configurable { changeRequireWhitelistedAsset(boolean); changeRequireWhitelistedAssetProvider(boolean); changeRequireAssetValidation(boolean); changeRequireSaleStrategyPerAsset(boolean); changeRequireSameCurrencyPerAsset(boolean); changeRequireAssetSubdomain(boolean); changeSupportsStakingSlashing(boolean); } /** * Represents a user who interacts with the contract */ abstract class User { marketplace: MarketplaceContract; // web3/ethers contract instance address: string; constructor(marketplace: MarketplaceContract) { this.marketplace = marketplace; }; abstract getAddress(): string; } /** * Abstract Role access */ interface AccessController { add(): Promise<any>; remove(): Promise<any>; } /* * Concrete Roles Access in the marketplace */ class AssetManagerController extends User implements AccessController { constructor(address: string, contract: ContractWrapper) { super(contract); this.address = address; }; async add() { this.marketplace.addAssetManager(this.getAddress()); }; async remove(){ this.marketplace.addAssetManager(this.getAddress()); }; getAddress() { return this.address } } class FinanceManagerController extends User implements AccessController { constructor(address: string, contract: ContractWrapper) { super(contract); this.address = address; }; async add() { // add administrator role into the marketplace this.marketplace.addFinanceManager(this.getAddress()); }; async remove(){ this.marketplace.addFinanceManager(this.getAddress()); }; getAddress() { return this.address } } // This can be decoupled using a Factory method // that creates the role access // classes for us class MarketplaceAccesControl implements AccessControlable { accessController: AccessController; marketplace: Contract; // web3/ethers contract instance addAssetManager(_assetManagerAddr: string) { this.accessController = new AssetManagerController(_assetManagerAddr, contract); this.accessController.add(); } removeAssetManager(_assetManagerAddr: string) { this.accessController = new AssetManagerController(_assetManagerAddr, contract); this.accessController.remove(); } // more methods... } class Marketplace implements IMarketplace { marketplace: Contract; //web3/ethers contract instance accessController: AccessControlable; configurer: Configurable; acceptedCurrencies: string[]; assetValidators: Array<any>; // more properties... constructor ( marketplace: Contract, //web3/ethers contract instance configurer: Configurable ) { this.marketplace = marketplace; this.configurer = configurer; } addAcceptedCurrency(_tokenERC20: string) {} removeAcceptedCurrency(_tokenERC20: string) {} withdraw(_to: string, amount: number) {} addAssetValidatorDelegate(_account: string) {} removeAssetValidatorDelegate(_account: string) {} assignRnsDomain(_rnsDomainNode: any) {} assignRnsNameGenerationStrategy(_strategy: string) {} registerAsset(_asset: string) {} validatorDelegateApprovesAsset(assetValidator: string, _asset: string){} validatorDelegateRejectAsset(assetValidator: string, _asset: any) {} assignOrderIdGeneratorStrategy(_strategy: string) {} sellAsset(_asset: string) {} } ``` ## Proposal #3 (Merge Proposal 1 & 2) ```typescript= /* Probably we need to segregate interfaces here */ interface IMarketplace { changeRequireWhitelistedAsset(requireWhitelistedAsset: boolean) : Promise<boolean>; changeRequireWhitelistedAssetProvider(requireWhitelistedAssetProvider: boolean) : Promise<boolean>; changeRequireAssetValidation(requireAssetValidation: boolean) : Promise<boolean>; changeRequireSaleStrategyPerAsset(requireSaleStrategyPerAsset: boolean) : Promise<boolean>; changeRequireSameCurrencyPerAsset(requireSameCurrencyPerAsset: boolean) : Promise<boolean>; changeRequireAssetSubdomain(requireAssetSubdomain: boolean) : Promise<boolean>; changeSupportsStakingSlashing(supportStakingSlashing: boolean) : Promise<boolean>; whitelistAsset(address: string) : Promise<boolean>; removeAssetFromWhitelist(address: string) : Promise<boolean>; isWhitelistedAsset(address: string) : Promise<boolean>; addAcceptedCurrency(tokenERC20: string) : Promise<boolean>; removeAcceptedCurrency(tokenERC20: string) : Promise<boolean>; withdraw(to: string, amount: number) : Promise<boolean>; addAssetValidatorDelegate(account: string) : Promise<boolean>; removeAssetValidatorDelegate(account: string) : Promise<boolean>; assignRnsDomain(rnsDomainNode: string) : Promise<boolean>; assignRnsNameGenerationStrategy(strategy: string) : Promise<boolean>; //registerAsset(Asset _asset) external; validatorDelegateApprovesAsset(assetValidator: string, asset: string) : Promise<boolean>; validatorDelegateRejectAsset(assetValidator: string, asset: string): Promise<boolean>; assignOrderIdGeneratorStrategy(strategy: string) : Promise<boolean>; sellAsset(asset: string) : Promise<boolean>; } interface AccessControlable { addAdministrator(_adminAddr: string): Promise<void>; removeAdministrator(_adminAddr: string):Promise<void>; addAssetManager(_assetManagerAddr: string):Promise<void>; removeAssetManager(_assetManagerAddr: string): Promise<void>; addAssetValidatorManager(_assetValidatorManagerAddr: string): Promise<void>; removeAssetValidatorManager(_assetValidatorAddr: string): Promise<void>; addAssetValidator(_assetValidatorManagerAddr: string): Promise<void>; removeAssetValidator(_assetValidatorAddr: string); addFinance(_financeAddr: string): Promise<void>; removeFinance(_financeAddr: string): Promise<void>; addFinanceManager(_financeManagerAddr: string): Promise<void>; removeFinanceManager(_financeManagerAddr: string): Promise<void>; addAssetProviderManager(_assetProviderManagerAddr: string): Promise<void>; removeAssetProviderManager(_assetProviderManagerAddr: string): Promise<void>; addAssetProvider(_assetProviderAddr: string): Promise<void>; removeAssetProvider(_assetProviderAddr: string): Promise<boolvoidean>; } class Marketplace implements IMarketplace, AccessControlable { marketplace: IContract; //web3/ethers contract instance acceptedCurrencies: string[]; assetValidators: Array; rnsNameGenerationStrategy: any; private assetWhitelist : AssetWhitelist; // more properties... constructor ( marketplace: IContract //web3/ethers contract instance contractAddress: string, ) { this.marketplace = marketplace; } // implements AccessControlable interface addAssetManager(address: string) { const assetManagerController = AccessControllerFactory.getAccessController( AccessControllerType.ASSET_MANAGER, contract, ); assetManagerController.add(address); } removeAssetManager(address: string) { const assetManagerController = AccessControllerFactory.getAccessController( AccessControllerType.ASSET_MANAGER, contract, ); assetManagerController.remove(address); } addFinanceManager(address: string) { const financeManagerController = AccessControllerFactory.getAccessController( AccessControllerType.FINANCE_MANAGER, contract, ); financeManagerController.add(address); } removeFinanceManager(address: string) { const financeManagerController = AccessControllerFactory.getAccessController( AccessControllerType.FINANCE_MANAGER, contract, ); financeManagerController.remove(address); } // more methods... addAcceptedCurrency(_tokenERC20: string): Promise<void> {} removeAcceptedCurrency(_tokenERC20: string): Promise<void> {} withdraw(_to: string, amount: number): Promise<void> {} addAssetValidatorDelegate(_account: string): Promise<void> {} removeAssetValidatorDelegate(_account: string): Promise<void> {} assignRnsDomain(_rnsDomainNode: any): Promise<void> {} assignRnsNameGenerationStrategy(_strategy: string): Promise<void> {} registerAsset(_asset: string): Promise<void> {} validatorDelegateApprovesAsset(assetValidator: string, _asset: string): Promise<void>{} validatorDelegateRejectAsset(assetValidator: string, _asset: any): Promise<void> {} assignOrderIdGeneratorStrategy(_strategy: string) {} sellAsset(_asset: string): Promise<number> {} // implements configuration interface changeRequireWhitelistedAsset(requireWhitelistedAsset: boolean) : Promise<boolean>; changeRequireWhitelistedAssetProvider(requireWhitelistedAssetProvider: boolean) : Promise<boolean>; changeRequireAssetValidation(requireAssetValidation: boolean) : Promise<boolean>; changeRequireSaleStrategyPerAsset(requireSaleStrategyPerAsset: boolean) : Promise<boolean>; changeRequireSameCurrencyPerAsset(requireSameCurrencyPerAsset: boolean) : Promise<boolean>; changeRequireAssetSubdomain(requireAssetSubdomain: boolean) : Promise<boolean>; changeSupportsStakingSlashing(supportStakingSlashing: boolean) : Promise<boolean>; } interface IWhitelist { add(address: string) : Promise<boolean>; remove(address: string) : Promise<boolean>; isWhitelisted(address: string) : Promise<boolean>; } class AssetWhitelist implements IWhitelist { // TODO: implement } /* Suggestion class CurrencyWhitelist implements IWhitelist { } class AssetValidatorDelegateWhitelist implemts IWhitelist { } */ /** * Abstract Role access */ interface AccessController { add(address, marketplace): Promise<void>; remove(address, marketplace): Promise<void>; } /* * Concrete Roles Access in the marketplace */ class AssetManagerController implements AccessController { private marketplace : Contract; constructor(marketplace: Contract) { this.marketplace = marketplace; }; add(address) { return this.marketplace.addAssetManager(address); }; remove(address){ return this.marketplace.removeAssetManager(address); }; } class FinanceManagerController implements AccessController { private marketplace : Contract; constructor(marketplace: Contract) { this.marketplace = marketplace; this.address = address; }; add(address) { return this.marketplace.addFinanceManager(address); }; remove(address){ return this.marketplace.removeFinanceManager(address); }; } enum AccessControllerType { ASSET_MANAGER; FINANCE_MANAGER; ASSET_VALIDATOR_MANAGER ASSET_PROVIDER_MANAGER; ADMINISTRATOR; OWNER; } class AccessControllerFactory { public static getAccessController( type: AccessControllerType, marketplace: IContract, ): AccesController { switch(type){ case AccessControllerType.ASSET_MANAGER: return new AssetManagerController(marketplace); case AccessControllerType.FINANCE_MANAGER: return new FinanceManagerController(marketplace); // more types... default: return null; } } } // Contract interactor suggestion class IContract { addAcceptedCurrency(address: string) : Promise<boolean>; } // Discuss: Basically a middleman // - https://refactoring.guru/smells/middle-man // Ideally, we would like to use the same interface used by Ethers (probs doesn't exist) // Or use TypeChain ? class implements IContract { private ethers : any; constructor(ethers: any) { this.ethers = ethers; } addAcceptedCurrency(address: string) : Promise<boolean> { this.ethers.addAcceptedCurrency(address); } } ```