SIP-19: Implement, adopt and enforce Samurai Chat

Motivation

This proposal suggests for the protocol to adopt and enforce a set of smart contracts (specified below) that enable, facilitate and provide access to the subscription of a newly developed product called Samurai Chat. Samurai Chat represents a brand new SaaS (Software-as-a-Service) feature and solution that intends to transcend the conventional ChatGPT experience. The product introduces a suite of custom features, including a prompt library, history search, and folders.

Samurai Chat aligns with the protocol's mission to leverage cutting-edge technology and grow its ecosystem and suite of products. The proposal and the associated implementation of the Samurai Chat feature further attempts to ensure that the Samurai protocol remains at the forefront of decentralized AI solutions. The implementation of this proposal shall further bolster the utility of the Samurai Node NFTs and the xHNR token.

The feature shall be exclusively available to Samurai Node NFT holders and xHNR token holders.

Specification

The scope of the product comprises of the following elements.

  • Samurai Chat Interface

    • A newly developed frontend interface specifically designed for interaction with the OpenAI GPT model.

    • Tailored UI/UX to enhance user interaction and provide a seamless experience.

    • Incorporation of Samurai’s branding and design elements to maintain protocol identity.

  • Exclusive Access

    • Access to Samurai Chat shall be exclusively facilitated and provided to Samurai Node NFT holders and xHNR token holders. This would further reinforce the increasing token and NFT utility.

  • Custom Features

    • Prompt Library: A comprehensive collection of pre-designed and user-contributed prompts for diverse applications, ranging from simple queries to complex scenarios.

    • History Search: Allows users to search and filter through their previous interactions with the AI, providing a valuable tool for tracking conversations and retrieving information.

    • Folders: Enables users to categorize and store their interactions in an organized manner, facilitating efficient information management and retrieval.

  • API Key Requirement

    • Users shall supply their own OpenAI API keys, linking their use of Samurai Chat to their OpenAI account. This would ensure that each user's interaction is based on their specific OpenAI subscription, as well as it ensures the experience is personalized and secure.

  • Subscription Model

    • Enforce a monthly (28 days) subscription fee of 100,000 xHNR

    • To access the subscription, the user shall hold 100x Samurai Node NFTs

    • This fee structure shall balance accessibility with the premium nature of the service

    • The collected subscription fees shall be directed towards the protocol's treasury

Implementation Approach

  • Frontend Development

    • Development of a user-friendly interface with advanced and custom features.

  • Subscription Management

    • Creating a system to handle subscriptions, including sign-ups, and renewals

    • Implementation of smart contracts to manage the collection and allocation of subscription fees in xHNR tokens.

The proposal primarily pertains to the adoption of the smart contracts that would enable and facilitate the product’s subscription model, as its enforcement is enacted on-chain.

The proposed Solidity code / smart contracts

SamuraiSubscription.sol

// SPDX-License-Identifier: MIT pragma solidity 0.8.13;

import "@openzeppelin/contracts/access/Ownable.sol"; import "@openzeppelin/contracts/interfaces/IERC20.sol"; import "@openzeppelin/contracts/interfaces/IERC721Enumerable.sol"; import "@openzeppelin/contracts/security/Pausable.sol"; import "@openzeppelin/contracts/utils/math/SafeMath.sol"; import "./IUniswapV2Router02.sol";

contract SamuraiSubscription is Ownable, Pausable { IERC20 public xhnr; IERC721Enumerable public hnrNodes; IUniswapV2Router02 public uniswapV2Router;

uint256 public threshold; uint256 public feeRate; uint256 public subscriptionCost;

mapping(address => uint256) public subscriptions;

using SafeMath for uint256;

constructor( address _xhnr, address _hnrNodes, address _uniswapV2Router, uint256 _threshold, uint256 _feeRate, uint256 _subscriptionCost ) { xhnr = IERC20(_xhnr); hnrNodes = IERC721Enumerable(_hnrNodes); uniswapV2Router = IUniswapV2Router02(_uniswapV2Router); threshold = _threshold; feeRate = _feeRate; subscriptionCost = _subscriptionCost; }

receive() external payable virtual { // }

function subscribe() external whenNotPaused { address sender = msg.sender; require(hnrNodes.balanceOf(sender) >= threshold, "Contract: not a holder!"); uint256 timeNow = block.timestamp; uint256 existingSubscription = subscriptions[sender]; require( timeNow > existingSubscription, "Contract: subscription already active!" ); // charge the subscription xhnr.transferFrom(sender, address(this), subscriptionCost); uint256 fee = subscriptionCost.mul(feeRate).div(100); // take the fee swapTokensForEth(fee); // 28 days in seconds uint256 oneMonth = 28 * 24 * 60 * 60; uint256 subscriptionEnd = timeNow + oneMonth;

subscriptions[sender] = subscriptionEnd;

}

function isSubscribed() external view returns (bool) { address sender = msg.sender; uint256 timeNow = block.timestamp; uint256 subscriptionEnd = subscriptions[sender];

return timeNow <= subscriptionEnd;

}

function swapTokensForEth(uint256 tokenAmount) private { address[] memory path = new address; path[0] = address(xhnr); path[1] = uniswapV2Router.WETH();

xhnr.approve(address(uniswapV2Router), tokenAmount);

uniswapV2Router.swapExactTokensForETHSupportingFeeOnTransferTokens(
  tokenAmount,
  0, // accept any amount of ETH
  path,
  address(this),
  block.timestamp
);

}

// owner related function setSubscriptionCost(uint256 _subscriptionCost) external onlyOwner { subscriptionCost = _subscriptionCost; }

function setThreshold(uint256 _threshold) external onlyOwner { threshold = _threshold; }

function setFeeRate(uint256 _feeRate) external onlyOwner { feeRate = _feeRate; }

function release() external onlyOwner { xhnr.transfer(owner(), xhnr.balanceOf(address(this))); }

function releaseNative() external onlyOwner { payable(owner()).transfer(address(this).balance); }

function pause(bool en) external onlyOwner { en ? _pause() : _unpause(); } }

Last updated