https://streamhacker.com
StreamHacker | Weotta be hacking
Weotta be hacking
StreamHacker | Weotta be hacking StreamHacker Search Primary Menu Skip to contentAboutLinks Search for: streamhacker-technologiesProgrammatic SEO: A Case Study August 15, 2024 Jacob Leave a commentI recently worked with a client at the beginning of their SEO strategy. This case study outlines our analysis and recommendations to improve their search visibility using programmatic SEO techniques. I wrote up my general recommendations for anyone interested in Programmatic SEO here.Initial AssessmentThe client had a significant amount of unique content, but it was not being indexed properly due to a number of technical issues. Some of the problems we identified:Most of the content lived on a subdomain, not the primary domain – not a huge issue, if everything else is goodThe primary domain had pages with redirects to the subdomain, but they were 302 instead of 301 redirectsNeither the subdomain nor the primary domain had a robots.txt or sitemap.xmlPage content was largely being loaded through React Javascript calls, instead of being pre-rendered on the page, leading to various page speed issuesNo directory index pagesNo inter-linking across pagesI showed them the Zapier app directory, which is an excellent example of how to generate many thousands of pages with unique content, as well as directory index pages for browsing the content. One interesting question they had was “are more pages better?”. The answer is: yes, more pages are better, but only if each page has mostly unique content. All the pages can follow the same template structure, but if you diff across pages, there should be plenty of significant content differences.RecommendationsBased on our findings, we figured out a set of prioritized recommendations, starting with the easiest wins:Implement robots.txt and sitemap.xml: This is the first step to guide search engines on how to crawl and index the site. Both the top-level domain (TLD) and subdomains need these files. A simple robots.txt that allows all user agents is fine to start with, as long as there’s something. And you can also link to the sitemap.xml in the robots.txt.Switch to 301 Redirects: We advised the client to use 301 redirects instead of 302 to ensure that link equity is passed correctly, which is crucial for SEO.Optimize On-Page Elements: Titles, meta descriptions, and H1 tags should be optimized to highlight the unique keywords on each page. Each page should have a single H1 tag that closely matches the page title.Use SEO Tools: Setup Google Search Console, Bing Webmaster Tools, and an SEO tool like ahrefs to see how the content is being indexed and how well it is performing. Most people don’t realize that Bing is the backend search index for most alternative search engines like duckduckgo.com, so it’s important to factor them in to improve your reach.Generate Directory Index Pages: Create index pages from top-level category items to enhance navigation and SEO.Inter-Linking Across Content: Create links between related pages using categories, or any other content relationships.Static HTML Generation: Instead of loading content through React in real-time, generating static HTML pages can greatly improve load times and indexing quality.Load Balancer Implementation: By putting a load balancer in front of the main domain, they can then point a specific path to the subdomain, thus eliminating the need for a visibly separate subdomain and 301 redirects.Mobile Optimization: Ensuring the site is optimized for mobile rendering is crucial for improving search ranking. All search engines now prioritize mobile pages and mobile rendering.ConclusionProgrammatic SEO can provide substantial improvements in search visibility and user experience when implemented correctly. Generating thousands of pages programmatically is a great way to scale your content and visibility. But it’s crucial to get the technical implementation correct, otherwise the search engines will not properly index the content, and your pages just won’t show up in search results.If you’d like some specialized help on your programmatic SEO strategy, feel free to reach out to me at Streamhacker Technologies.Share this:XRedditLinkedInPocketFacebookThreadsBlueskyLike this:Like Loading...mobilesearchseo programming, securitySalt Recipe for Creating a MySQL User with Grants for Scalyr June 20, 2024 Jacob Leave a commentSalt is a great tool for managing the configuration of many servers. And when you have many servers, you should also be monitoring them with a tool like Dataset (aka Scalyr). The scalyr agent can monitor many things, but in this example, I’m going to show you how to create a MySQL user for the scalyr agent with just the right amount of permissions.Salt Formula{% set scalyr_user = salt['pillar.get']('scalyr:mysql:user', 'scalyr-agent-monitor') %} mysql_scalyr_user: mysql_user.present: # - host: localhost - name: {{ scalyr_user }} - password: {{ pillar['scalyr']['mysql']['password'] }} mysql_grants.present: - grant: 'process, replication client' - database: '*.*' # - host: localhost - user: {{ scalyr_user }} - require: - mysql_user: {{ scalyr_user }}Salt uses yaml with jinja templating to define states. This template does the following:Creates a MySQL user for scalyrGrants permissions for that scalyr user to access MySQL process & replication metrics on all databasesYou can view the full range of options for the mysql_user and mysql_grants states if you need to customize it more.Pillar ConfigurationThe above salt recipe requires a corresponding pillar configuration that looks like this:scalyr: mysql: user: scalyr-agent-monitor password: RANDOM_PASSWORDScalyr Agent ConfigurationThen in your scalyr agent JSON, you can use a template like this:{ logs: [{ path: "/var/log/mysql/error.log", attributes: {parser: "mysql_error"} }, { path: "/var/log/mysql/slow.log", attributes: {parser: "mysql_slow"} }], monitors: [{ module: "scalyr_agent.builtin_monitors.mysql_monitor", database_username: "{{ salt['pillar.get']('scalyr:mysql:user') }}", database_password: "{{ salt['pillar.get']('scalyr:mysql:password') }}", database_socket: "/var/run/mysqld/mysqld.sock" }] }How to use itIf you’re already familiar with salt, then hopefully this all makes sense. Let’s say you named your state mysql_user in a scalyr state directory. Then you could apply it like this:salt SERVER_ID state.sls scalyr.mysql_userAnd now you have a MySQL user just for scalyr. This same idea can likely be applied to any other MySQL monitoring program.If you’d like some help automating your server configuration and monitoring using tools and formulas like this, contact us at Streamhacker Technologies.Share this:XRedditLinkedInPocketFacebookThreadsBlueskyLike this:Like Loading...jinjamonitoringmysqlpermissionssaltscalyrsecurityyaml javascriptA Quick Simple Way to Download All the Images on a Page October 22, 2023 Jacob Leave a commentYou don’t need to write a web scraper to do this, just some simple code and standard linux/unix commands.Open the page in your web browserOpen the Developer ToolsPaste in the following Javascriptvar images = document.getElementsByTagName('img'); var srcList = []; for(var i = 0; i < images.length; i++) { srcList.push(images[i].src.split('?', 1)[0]); } srcList.join('\n');Create a folder to store the imagesCopy the text output from above into a file & save as images.txt in your folderInspect images.txt to make sure it looks rightRun the following commands in a terminal, from your foldercat images.txt | sort | uniq > uniq_images.txt wget -i uniq_images.txtNow all the images from that page should be in the folder you created.Share this:XRedditLinkedInPocketFacebookThreadsBlueskyLike this:Like Loading...bashjavascriptscrapingwget streamhacker-technologiesDeveloping an Etsy App – Getting Started October 19, 2023 Jacob Leave a commentI’m working on an Etsy app for some client work (an Etsy listing scheduler) and just getting started is quite a process. So I’m documenting it here for anyone else that may be interested in creating an app for Etsy.Step 1: Have a Real Etsy AccountTo begin, you’ll need a real Etsy account. If you don’t have one already, sign up for an account on the Etsy website.Step 2: Create a Test Etsy StoreNext, create a test Etsy store that is real enough for testing purposes. You need to create a real listing, even if it’s a digital item that is some throw-away photo. You also need to connect a real bank account to receive payouts. This Etsy store will be your test environment for developing your app.Step 3: Set Store to Developer ModeTo ensure that your listings are not visible in Etsy’s search, set your store to Developer mode. Only do this if you’re working through your own personal account. Do not do this for a real Etsy shop.Step 4: Create a WebpageCreate a webpage for your Etsy app. This will serve as the main interface for users to interact with your app. But for now, it’s really for the Etsy app approval team, so they can learn about the purpose of your app.Step 5: Review Etsy’s Terms and ConditionsBefore proceeding further, carefully review Etsy’s terms and conditions. A specific restrictions they have is that you cannot use the term “Etsy” in the name of your app or the title/heading of your website. You should also include the following text on your website: The term ‘Etsy’ is a trademark of Etsy, Inc. This application uses the Etsy API but is not endorsed or certified by Etsy, Inc.Step 6: Register for the Etsy APIRegister a new Etsy app. This app will be directly tied to your Etsy account, which you created in Step 1. You must also agree to their API Testing Policy.Step 7: Contact Etsy Developer SupportReach out to the Etsy Developer Support team by emailing developers@etsy.com. This shouldn’t be necessary since you registered in the previous step, but if you actually want to get a response and your app approved, you need to email them. Someone will review your website and registration to ensure it complies with their terms before approving your app.Step 8: Start with Personal TokenInitially you will only get a personal API token, which means you can only interact with your own store through the API. This will allow you to test and iterate on your app’s functionality. You’ll need to actually create an initial version of your app before requesting commercial access.Step 9: Provisional UsersAs you progress with your app development, you may want to test your app on other stores. You can add provisional users with a special API.Step 10: Create Material for Etsy ReviewPrepare all the necessary materials, such as documentation and screenshots, for Etsy’s commercial API review process. This might include OAuth permissions required, and API calls that your app makes. These materials will help Etsy understand and evaluate your app.Step 11: Request Commercial AccessOnce your app is ready for wider usage, request commercial access from Etsy. This will allow anyone to authenticate and use your app via OAuth, once you’re approved.Share this:XRedditLinkedInPocketFacebookThreadsBlueskyLike this:Like Loading...apietsytesting streamhacker-technologiesSentiment Analysis Survey 2023 September 8, 2023 Jacob Leave a commentDo you use a sentiment analysis API or tool? Or have you considered using one but couldn’t find one that fit your needs?If the answer to either of those questions is yes, then please fill out this Sentiment Analysis Survey. I’d like to learn more and hear directly from users and customers of sentiment analysis tools. Thanks for your time.Share this:XRedditLinkedInPocketFacebookThreadsBlueskyLike this:Like Loading...nlpsentiment streamhacker-technologies5 Lesser Known Risk Factors in Payment Fraud August 22, 2023 Jacob Leave a commentWhen you’re analyzing payments to determine if they are fraudulent, what should you look for? Stripe Radar is great at blocking the more obvious fraudulent payments, and allowing the payments that are clearly not fraud, but what about the payments that are in between? There are a number of less obvious factors you can look at to determine whether a payment is fraud.How to Decide if a Payment Under Review is FraudulentHere are 5 lesser known factors we’ve identified when working with clients of Streamhacker Technologies. We’ll describe each of these in more detail belowHistory of adding & removing cardsSpecific fraud insightsFast plan upgradesLack of product usageMultiple IPs and payment attemptsWhile this article uses examples from Stripe, these factors can apply to almost any payment platform.History of adding & removing cardsWhen a customer uses multiples cards to make payments over a relatively short period of time, that’s a big warning sign of card testing. When combined with fast plan upgrades, multiple IPs, and lack of product usage, then you can be confident it’s fraud.Much of the time, Stripe will show this behavior in the Related Payments section of a charge. You can see an example here.However, sometimes you need to go into the customer profile to get the full picture. In the Recent Activity section, you can see if the customer added a new card. Here’s an example of what it looks like when someone changes cards within ~1 day of signing up.On its own, this is suspect but not necessarily fraud. However, if there’s more than 2 cards, that’s quite suspect. Also very suspect if the cards come from multiple countries. If you click on Show details for any of the cards, you can see the countries.Above you can see 2 different cards from 2 different countries. And in this case, the customer’s IP address was in a third country. Very suspicious behavior.Specific Fraud InsightsOn a Stripe charge payment, there’s a Fraud Insights button that shows you various fraud factors. Three that we’ve found to be useful are shown below.A low authorization rate and more than 0 declines associated with the customer’s email are significant fraud indicators. The name-email similarity match is a small additional indicator on top. These insights are most useful when combined with the other indicators discussed here.Fast Plan UpgradesA “fast plan upgrade” is when someone subscribes to the lowest plan of your service, then upgrades to one of your highest plans within a few minutes. This may be another form of card testing. Maybe your lowest plan is $10 and your highest plan is $100 – those are very different purchase amounts, and a card tester may want to find out if the card that works at a low amount can also be used for larger purchases. If the first upgrade attempt fails, and they switch cards to try again, fraud risk looks a lot more likely. These related payments show an example of this exact behavior.Here’s what happened:Attempted to purchase low level plan at $10, but that failedSwitched cards and tried again, Stripe risk score was still 0One minute later, successfully upgraded to a higher plan, and got a risk score of 47, which Stripe still considers “normal”1 day later, tried to upgrade again to an even higher plan, but that failed with a higher risk scoreNote: 2 payments are showing as Refunded because they were successful until being refunded as fraud.Lack of Product UsageIf a new customer doesn’t use your product much right away, that’s ok. But if they also change cards and/or try to upgrade plans without using your product at all, that’s suspicious. In Streamlining Stripe Reviews with Webhooks and Zapier I described how we helped a client highlight product usage metrics as part of their Stripe review process. Getting some product usage metrics into your Stripe charge metadata is very useful for fraud analysis, so you can quickly look at all your risk factors in one place.Multiple IPs and Payment AttemptsMany people use VPNs and proxy servers for very legitimate reasons. And sometimes people are traveling. Just because the credit card country doesn’t match the IP country, or there’s a low authorization rate for an IP address, that doesn’t necessarily mean a payment is fraud. But when the IP address of a customer changes over a short period of time, and they make multiple payment attempts from multiple IP addresses, that’s unusual. Stripe’s Related Payments section helps to show this kind of behavior.ConclusionDeciding whether a payment is fraud can be tricky, and is not always obvious. But there are risk indicators you can look for, and when you see multiple indicators together, you can be more confident in a fraud assessment. Conversely, if you only see one of these indicators, then a payment likely isn’t fraud. Whatever your assessment is, take detailed notes. Stripe’s charge UI has a nice feature where you can leave a note for future reference – be sure to use this so you have a history of why you made a decision, and can revisit these decisions in the future, when you have more information.Here are some helpful links from Stripe on identifying and preventing payment fraud:Identifying potential fraudCommon types of online fraudProtect yourself from card testingBest practices for preventing fraudIf you think your team or company needs help managing payment fraud, contact Streamhacker Technologies to see what we can do for you.Share this:XRedditLinkedInPocketFacebookThreadsBlueskyLike this:Like Loading...fraudpaymentsriskstripe streamhacker-technologiesStreamlining Stripe Reviews with Webhooks and Zapier August 2, 2023 Jacob Leave a commentIf your company is handling payments through Stripe, you’re likely familiar with their Radar product, which helps protect you from payment fraud. And if you’re using Stripe Radar, then you may have experienced the issue of receiving a lot of Stripe Review emails. Handling all of these reviews can be time-consuming and difficult to manage effectively, and the Stripe review email doesn’t provide any useful information on its own. Getting one email for each review means they can pile up, and sometimes you might go through all the reviews within the Stripe dashboard, but the emails are still in your inbox demanding attention, as if the review is still open. In this case study, we’ll explore how Streamhacker Technologies helped a company tackle this problem using the Stripe API, custom webhooks, and Zapier.The Problem: Too Many Stripe Review EmailsThe billing team found themselves inundated with a high volume of individual review emails from Stripe Radar. Although these emails were meant to highlight potentially fraudulent charges, they often caused annoyance and frustration. The team observed that by the time they opened some reviews, they were already closed due to another team members action, or by automatic fraud controls that got triggered after the review email was sent. In most other cases, the information they needed to make a fraud determination was found elsewhere, in a separate system, with no direct links from Stripe. This back and forth was an inefficient use of their time and attention, and they needed a better solution.The Solution: Webhooks and ZapierWe devised a solution that utilized Stripe’s API, custom webhooks, and Zapier to streamline the payment review process.First, we created a custom webhook to retrieve additional customer information associated with each charge. This information helped indicate which reviews were more actionable, by including things like customer age and product usage metrics.Then, we created a Zap to do the following:Get all open Stripe reviewsFor each Stripe reviewCall the custom webhook to get additional informationAdd that information as metadata to the Stripe charge under review, including links to systems with additional information about the customerAppend select information to a DigestSend a single digest email every morning, containing all the open Stripe reviews, with specific indicators to help decide which reviews require attentionCreate a mail rule to automatically close the individual Stripe review emailsThe Result: Efficient Stripe Review ProcessingAfter implementing this solution, the team noticed a significant reduction in time spent processing fraud reviews. They were able to quickly identify reviews that required action, analyze the payments and customer behavior faster, and they no longer wasted time opening reviews that had already been closed.ConclusionSimple custom webhooks + Zapier = more efficient business operations. In this case, we were able to help the team save significant time and attention by improving their existing payment review process, which freed them up to focus on other business problems.If you think your team or company might benefit from a similar solution, contact Streamhacker Technologies to see what we can do for you.Share this:XRedditLinkedInPocketFacebookThreadsBlueskyLike this:Like Loading...fraudpaymentsstripewebhookszapier linksGPT, LLM, and Langchain Links April 15, 2023 Jacob Leave a commentThis post is a compilation of links I’ve seen recently related to running GPT or other language models locally and/or with customizations. The ecosystem around GPT & Large Language Models is changing quickly but these are good places to get started.Run a Chatbot LocallyHow You Can Install A ChatGPT-like Personal AI On Your Own Computer And Run It With No Internet.LlamaChat | Chat with your favourite LLaMA LLM modelsDolly is a free, open source, ChatGPT-style AI modelRun a Chatbot on PDFs using LangchainLangchain PDF QAGPT4 PDF Chatbot LangchainMake a QA Chatbot on your own textOpenAI Cookbook: Question Answering Using EmbeddingsGPT4-allShare this:XRedditLinkedInPocketFacebookThreadsBlueskyLike this:Like Loading...chatbotchatgptdollygptlangchainllamallmnlppdfpython contractsDelegate.cash Contract Review October 8, 2022 Jacob Leave a commentDelegate.cash is a new protocol intended to make airdrops and token claims safer. If you have vaulted NFTs, you strongly want to avoid using that vault wallet to interact with any contracts. And if you have to move NFTs back & forth between your vault wallet and hot wallet to make claims, then you probably won’t. foobar, one of the authors of delegate.cash, has a great write up on why this kind of protocol needs to exist. Let’s take a look at the code…(note that the code blocks below contain some intermixed code from different parts of DelegationRegistry.sol and IDelegationRegistry.sol for clarity of explanation)DelegationThere are 3 types of delegation:ALL – the vault wallet delegates all actions to the delegate walletCONTRACT – the vault wallet delegates all actions on a specific contract to the delegate walletTOKEN – the vault wallet delegates all actions for a specific token (on a specific contract) to the delegate walletThere are delegation functions for each of these delegation types that allow you to enable or disable the delegation (based on value). These functions must be called from the vault wallet (vault = msg.sender), but they are quite straightforward and safe:Compute a delegationHashSet DelegationInfo values for that delegationHashenum DelegationType { NONE, ALL, CONTRACT, TOKEN } function delegateForAll(address delegate, bool value) external override { bytes32 delegationHash = _computeAllDelegationHash(msg.sender, delegate); _setDelegationValues( delegate, delegationHash, value, IDelegationRegistry.DelegationType.ALL, msg.sender, address(0), 0 ); emit IDelegationRegistry.DelegateForAll(msg.sender, delegate, value); } function delegateForContract(address delegate, address contract_, bool value) external override { bytes32 delegationHash = _computeContractDelegationHash(msg.sender, delegate, contract_); _setDelegationValues( delegate, delegationHash, value, IDelegationRegistry.DelegationType.CONTRACT, msg.sender, contract_, 0 ); emit IDelegationRegistry.DelegateForContract(msg.sender, delegate, contract_, value); } function delegateForToken(address delegate, address contract_, uint256 tokenId, bool value) external override { bytes32 delegationHash = _computeTokenDelegationHash(msg.sender, delegate, contract_, tokenId); _setDelegationValues( delegate, delegationHash, value, IDelegationRegistry.DelegationType.TOKEN, msg.sender, contract_, tokenId ); emit IDelegationRegistry.DelegateForToken(msg.sender, delegate, contract_, tokenId, value); }Delegation HashThe delegationHash is where the authors got clever. Not only does the hash make delegation lookups faster and more efficient, it makes revoking delegations very cheap for gas. Every vault wallet has a numeric vaultVersion, which is initially 0. If this gets changed, then the delegationHash will change. There is a similar numeric delegateVersion for (vault, delegate) pairs. Revoking (covered more below) is just a matter of changing those version numbers, therefore changing the hash./// @notice A mapping of wallets to versions (for cheap revocation) mapping(address => uint256) internal vaultVersion; /// @notice A mapping of wallets to delegates to versions (for cheap revocation) mapping(address => mapping(address => uint256)) internal delegateVersion; function delegateForAll(address delegate, bool value) external override { bytes32 delegationHash = _computeAllDelegationHash(msg.sender, delegate); _setDelegationValues( delegate, delegationHash, value, IDelegationRegistry.DelegationType.ALL, msg.sender, address(0), 0 ); emit IDelegationRegistry.DelegateForAll(msg.sender, delegate, value); } function delegateForContract(address delegate, address contract_, bool value) external override { bytes32 delegationHash = _computeContractDelegationHash(msg.sender, delegate, contract_); _setDelegationValues( delegate, delegationHash, value, IDelegationRegistry.DelegationType.CONTRACT, msg.sender, contract_, 0 ); emit IDelegationRegistry.DelegateForContract(msg.sender, delegate, contract_, value); } function delegateForToken(address delegate, address contract_, uint256 tokenId, bool value) external override { bytes32 delegationHash = _computeTokenDelegationHash(msg.sender, delegate, contract_, tokenId); _setDelegationValues( delegate, delegationHash, value, IDelegationRegistry.DelegationType.TOKEN, msg.sender, contract_, tokenId ); emit IDelegationRegistry.DelegateForToken(msg.sender, delegate, contract_, tokenId, value); }Delegation InfoWhen delegating, a DelegationInfo object is created and stored by delegateHash. This DelegationInfo contains the following data:vault: the vault walletdelegate: the delegate or hot wallettype_: the DelegationType, which can be ALL, CONTRACT, or TOKENcontract_: the contract address for CONTRACT & TOKEN types, or the 0 address for ALLtokenId: the token number for the TOKEN type, or 0If value is false, then the delegateHash and corresponding DelegationInfo are deleted, thereby revoking a specific delegation. However, there are cheaper ways to revoke delegation, covered next./// @notice Info about a single delegation, used for onchain enumeration struct DelegationInfo { DelegationType type_; address vault; address delegate; address contract_; uint256 tokenId; } function _setDelegationValues( address delegate, bytes32 delegateHash, bool value, IDelegationRegistry.DelegationType type_, address vault, address contract_, uint256 tokenId ) internal { if (value) { delegations[vault][vaultVersion[vault]].add(delegateHash); delegationHashes[delegate].add(delegateHash); delegationInfo[delegateHash] = DelegationInfo({vault: vault, delegate: delegate, type_: type_, contract_: contract_, tokenId: tokenId}); } else { delegations[vault][vaultVersion[vault]].remove(delegateHash); delegationHashes[delegate].remove(delegateHash); delete delegationInfo[delegateHash]; } }RevokingThe contract provides very cheap ways to revoke delegation, by changing the vaultVersion or delegateVersion.revokeAllDelegates would be called from the vault wallet, and invalidates all delegations for that walletrevokeDelegate would also be called from the vault wallet, to invalidate a specific delegate walletrevokeSelf would be called from a delegate wallet, to invalidate all delegations from a specific vault walletAs mentioned above, instead of a complicated and gas expensive deletion of DelegationInfo, by incrementing a version number the delegation hash computation is changed. Technically this means the old DelegationInfo remains in the contract storage, but it is no longer accessible and cannot be used by any delegation lookups. There is also a theoretical limit of 2^256-1 for the number of times you can revoke and change either of the version numbers, but you’d have to try extremely hard and spend a lot of gas to do this.function revokeAllDelegates() external override { ++vaultVersion[msg.sender]; emit IDelegationRegistry.RevokeAllDelegates(msg.sender); } function revokeDelegate(address delegate) external override { _revokeDelegate(delegate, msg.sender); } function revokeSelf(address vault) external override { _revokeDelegate(msg.sender, vault); } function _revokeDelegate(address delegate, address vault) internal { ++delegateVersion[vault][delegate]; // For enumerations, filter in the view functions emit IDelegationRegistry.RevokeDelegate(vault, msg.sender); }Delegation LookupsThere are a number of different ways to lookup delegations. I’ve included the function signatures below, but not the complete functions, as they are complicated, highly optimized loops iterating over the delegations created by the functions covered above. They are view only functions and therefore safe to call.function getDelegatesForAll(address vault) external view returns (address[] memory delegates) function getDelegatesForContract(address vault, address contract_) external view override returns (address[] memory delegates) function getDelegatesForToken(address vault, address contract_, uint256 tokenId) external view override returns (address[] memory delegates) function getContractLevelDelegations(address vault) external view returns (IDelegationRegistry.ContractDelegation[] memory contractDelegations) function getTokenLevelDelegations(address vault) external view returns (IDelegationRegistry.TokenDelegation[] memory tokenDelegations) function checkDelegateForAll(address delegate, address vault) public view override returns (bool) function checkDelegateForContract(address delegate, address vault, address contract_) public view override returns (bool) function checkDelegateForToken(address delegate, address vault, address contract_, uint256 tokenId) public view override returns (bool)These functions are designed to be called from other contracts or dapps, to enable the delegation protocol. For example, if a delegate wallet wants to make a claim for a NFT, it could provide a vault wallet and tokenId to the claim contract. The claim contract would then do the following:Confirm the vault wallet owns tokenIdCall checkDelegateForToken(delegate, vault, contract_, tokenId)Proceed with the claim if checkDelegateForToken returns trueDeny the claim if checkDelegateForToken returns falseFor the best UX, you’d probably want to call these functions initially from the dapp, but for security, you definitely want to verify everything within the claim contract.Slither AnalysisUnfortunately, slither 0x00000000000076A84feF008CDAbe6409d2FE638B doesn’t work because the openzepplin contract dependency paths are different than how etherscan structures things. To make slither work, I did the following:git clone --recurse-submodules https://github.com/0xfoobar/delegation-registry.gitcd delegation-registry/libslither ../src/DelegationRegistry.sol --print human-summaryslither ../src/DelegationRegistry.solHuman summary of slither analysisAs you can see, it’s in pretty good shape. The 4 medium issues are all unused returns in _setDelegationValues when adding or removing delegation hashes from a set. The add and remove functions just return a bool if the set was changed, which doesn’t really matter for this contract, so ignoring the return value is totally fine.The 1 optimization issue is that checkDelegateForToken should be declared external. It is actually external in the IDelegationRegistry interface, so I’m not sure why it’s public in DelegationRegistry. Maybe it’s just a simple oversight, but not really a big deal either.Some of the informational issues are about inline assembly in some of the delegation lookups. Assembly should definitely be used with care. In this case, the assembly is in view only functions, in a way that helps reduce the computation cost.ConclusionDelegate.cash does exactly what it claims to, in a very straightforward way. However, it’s useless on its own. For this protocol to work, developers have to start including it in their own contracts and dapps, then recommending it to their users. The team behind delegate.cash are attempting to make an official standard with EIP-5639. But the best thing they can do is to provide easy to use libraries and sample code for integrating the protocol into other contracts and dapps. Ideally this would go as far as re-usable React components and a sample claim contract to demonstrate an entire user & wallet flow. If developers can essentially copy & paste to give their users more secure options, then adoption will be much easier.Share this:XRedditLinkedInPocketFacebookThreadsBlueskyLike this:Like Loading...airdropethnftslithertoken contractsCreyzies Contract Review April 23, 2022 Jacob Leave a commentCreyzies is the official companion collect to mfers, by Reylarsdam. Every mfers holder received an equivalent amount of Creyzies in a surprise airdrop on 4/20/2022, with all expenses paid by Sartoshi, the creator of mfers.The contract was written by West Coast NFT, who also wrote the mfers contract. Let’s take a look at the code, which begins with a giant ascii creyzie.Airdrop struct SendData { address receiver; uint256 amount; } ... function airdrop(SendData[] calldata sendData) external onlyRole(SUPPORT_ROLE) nonReentrant { uint256 ts = baseContractAddress.totalSupply(); // loop through all addresses for (uint256 index = 0; index < sendData.length; index++) { require(totalSupply() + sendData[index].amount <= ts, 'Exceeds original supply'); _safeMint(sendData[index].receiver, sendData[index].amount); } }The airdrop function receives an array of SendData structs, each of which specifies a receiver address and an amount of tokens to airdrop. Before this function was called, the devs did a snapshot of all mfers owner addresses, and counted how many mfers were owned by each address, to create this SendData array. Then they called this function 14 times to perform the airdrop, for a total gas cost of ~15 ETH. Sartoshi deposited 20 ETH to fund the airdrop, then withdrew 5 ETH when the airdrop was complete.The baseContractAddress is the mfers contract address, which you can see in the Constructor Arguments below the code. So the first line of the function gets the total number of mfers from the mfers contract. Then, looping through the array of SendData, there’s a requirement that the current supply + the amount to be minted is less than or equal to the total mfers supply. The amounts should have been calculated correctly during the snapshot, but this is a good way to enforce the supply limit. Finally, the amount of tokens is minted to the receiver address. The Creyzies contract uses ERC721A, so minting multiple tokens at a time is very efficient.There’s no token ownership checks at mint time, so this does all assume that the SendData was calculated correctly from a snapshot. Some addresses were explicitly left out of the airdrop – these were known smart contract addresses such as a Gnosis Safe or NiftyGateway. The devs knew the airdrop may not work for them, so they setup a claim site and implemented a redeem function.Redeem function setUnclaimedTokenIds(uint256[] calldata tokenIds) external onlyRole(SUPPORT_ROLE) { for (uint256 index = 0; index < tokenIds.length; index++) { unclaimedTokenIds[tokenIds[index]] = true; } } function redeem(uint256[] calldata tokenIds) external nonReentrant { uint256 numberOfTokens = tokenIds.length; for (uint256 index = 0; index < numberOfTokens; index++) { require(unclaimedTokenIds[tokenIds[index]], 'Token has already been claimed'); try baseContractAddress.ownerOf(tokenIds[index]) returns (address ownerOfAddress) { require(ownerOfAddress == msg.sender, 'Caller must own NFTs'); } catch (bytes memory) { revert('Bad token contract'); } unclaimedTokenIds[tokenIds[index]] = false; } uint256 ts = baseContractAddress.totalSupply(); require(totalSupply() + numberOfTokens <= ts, 'Exceeds original supply'); _safeMint(msg.sender, numberOfTokens); }Sartoshi posted claim instructions to redeem unclaimed tokens that were not airdropped. setUnclaimedTokenIds was called immediately after the airdrop, and sets a flag to true in unclaimedTokenIds. Then, when an owner goes to creyzies.art to claim their token(s), the redeem function is called. For each token attempting to be redeem, the function checks thatThe token hasn’t been claimed alreadyThe redeemer (msg.sender) owns the corresponding mfers token.If those checks pass, the token is marked as claimed. But before it is minted, there is one final check that minting these tokens won’t exceed the total mfers supply. The devs definitely put some extra effort into the contract to double check their work, to ensure that Creyzies tokens went to the right addresses and never exceeded mfers supply.RoyaltiesOften, royalties from sales are handled by secondary marketplaces, using info from royaltyregistry.xyz. However, this contract implements ERC2981, which defines a royalty standard specified in the contract. Like with mfers, the default royalties are set fairly low, to 2.5%. The royalties are sent to 0x750314177EF0a319DCdC959599C76D63964729f1, which appears to be another contract that splits the royalties automatically.ProvenanceLike with The Picaroons, a provenance hash was set before minting, to 3307bc0bd06029bba4a826856f2e19db62d6df5b7f70d447fa5262117256c46c. They also published a page proving the fairness of the drop.Token URIWhile some NFTs exist fully on chain, most are token numbers in a contract that point to a URI. This URI points to metadata, which is where to find the NFT image and properties. For mfers, the token URI is on ipfs, a distributed filesystem, where the JSON metadata for each mfers token is stored. Creyzies, though, currently points to a web app on heroku.The token URI for Creyzies #1 is https://minting-pipeline-10.herokuapp.com/1This is not great, because it means that the Creyzies metadata depends on a Heroku app running correctly and being available. ipfs, being distributed, is a more decentralized and resilient location, and any files stored there can’t be changed without changing the URI. Metadata being served from a web app can be changed very easily. Hopefully there’s a plan to put all the metadata into ipfs as well; the images are there already. If that is done correctly, then the devs can call setBaseURI with the new ipfs URI, just like they did to set the heroku base URI. However, if there is some other surprise planned that requires changing the metadata, then keeping the token URIs pointing to a web app make sense. function setBaseURI(string memory baseURI_) external onlyRole(SUPPORT_ROLE) { _baseURIextended = baseURI_; } /** * @dev See {ERC721-_baseURI}. */ function _baseURI() internal view virtual override returns (string memory) { return _baseURIextended; }Slither AnalysisIn order to make slither work this time, I had to tweak the code for one of the dependencies, crytic-compile. Using my branch, you can run slither to analyze the contract.$ slither 0x19bb64b80cbf61e61965b0e5c2560cc7364c6546 --print human-summaryThe issues can all be seen by running slither 0x19bb64b80cbf61e61965b0e5c2560cc7364c6546. I think the way this contract calls baseContractAddress.ownerOf within a try-catch block might be a little unusual, since slither says the return value is unused, when that’s not really the case. The other medium issues are within ERC721A._mint, and look ignorable. However there is an issue about costly operations inside a loop within ERC721A._mint, for _currentIndex = updatedIndex. This is within an unchecked block, which is the same thing that the Counters library does. The Azuki gas optimization analysis was pretty extensive, so there may not be any more that can be done to optimize gas in this case.ConclusionThis is a very nice & unexpected gift to all mfers holders, and it looks like the devs did excellent work to ensure accuracy and minimize airdrop costs. The Crezies art is cc0 just like mfers, and it’s definitely worth checking out more of Rey’s work.Share this:XRedditLinkedInPocketFacebookThreadsBlueskyLike this:Like Loading...airdropazukierc2981erc721erc721aethipfsjsonmetadatamfersnftprovenanceroyaltiesslithersoliditytokenPosts navigation 1 2 … 13 Next → Type your email… Subscribe Weotta be hackingPython NLTK CookbookBad Data HandbookSitesFletchStreamhacker TechnologiesText ProcessingPost Categoriesblog (2)books (10)contracts (6)design (6)erlang (11)insight-engines (2)javascript (7)links (24)papers (2)programming (14)python (62)security (2)streamhacker-technologies (5)talks (3)Uncategorized (2)weotta (4)Tag cloud authentication bayes bigrams chunking classification database design django doctest email erc721 erlang eth fab feature extraction forms javascript jquery json machinelearning make mercurial mfers mint mnesia mongodb nft nlp nltk nose otp parsing performance python redis security sentiment slither solidity statistics tagging templates testing token unittest Proudly powered by WordPress Go to mobile version %d
en
us
en-US
1731303421
https://streamhacker.com
Modificare il tuo sito?
Cosa fai?